Browse Source

Add ability to cache compiled GLSL shaders, remove unused ShaderUtilization

rdb 10 years ago
parent
commit
6eb460c359

+ 2 - 44
panda/src/display/graphicsEngine.cxx

@@ -1152,6 +1152,8 @@ extract_texture_data(Texture *tex, GraphicsStateGuardian *gsg) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void GraphicsEngine::
 void GraphicsEngine::
 dispatch_compute(const LVecBase3i &work_groups, const ShaderAttrib *sattr, GraphicsStateGuardian *gsg) {
 dispatch_compute(const LVecBase3i &work_groups, const ShaderAttrib *sattr, GraphicsStateGuardian *gsg) {
+  nassertv(sattr->get_shader() != (Shader *)NULL);
+
   ReMutexHolder holder(_lock);
   ReMutexHolder holder(_lock);
 
 
   CPT(RenderState) state = RenderState::make(sattr);
   CPT(RenderState) state = RenderState::make(sattr);
@@ -2334,50 +2336,6 @@ auto_adjust_capabilities(GraphicsStateGuardian *gsg) {
       }
       }
     }
     }
   }
   }
-
-  if (shader_auto_utilization && (shader_utilization != SUT_none)) {
-    display_cat.error()
-      << "Invalid panda config file: if you set the config-variable\n"
-      << "shader_auto_utilization to true, you must set the config-variable"
-      << "shader_utilization to 'none'.\n";
-    shader_utilization = SUT_none; // Not a fix.  Just suppresses further error messages.
-  }
-
-  if (shader_auto_utilization && !Shader::have_shader_utilization()) {
-    if (gsg->get_supports_basic_shaders()) {
-      Shader::set_shader_utilization(SUT_basic);
-    } else {
-      Shader::set_shader_utilization(SUT_none);
-    }
-  }
-
-  if ((Shader::get_shader_utilization() != SUT_none) &&
-      (!gsg->get_supports_basic_shaders())) {
-
-    // Overaggressive configuration detected
-
-    display_cat.error()
-      << "The 'shader_utilization' config variable is set, meaning\n"
-      << "that panda may try to generate shaders.  However, the video \n"
-      << "driver I'm trying to use does not support shaders.\n";
-
-    if (shader_utilization == SUT_none) {
-      display_cat.error()
-        << "The 'shader_utilization' setting did not come from the config\n"
-        << "file.  In other words, it was altered procedurally.\n";
-
-      if (shader_auto_utilization) {
-        display_cat.error()
-          << "It is possible that it was set by panda's automatic mechanisms,\n"
-          << "which are currently enabled, because 'shader_auto_utilization' is\n"
-          << "true.  Panda's automatic mechanisms assume that if one\n"
-          << "window supports shaders, then they all will.\n"
-          << "This assumption works for most games, but not all.\n"
-          << "In particular, it can fail if the game creates multiple windows\n"
-          << "on multiple displays with different video cards.\n";
-      }
-    }
-  }
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////

+ 6 - 0
panda/src/glstuff/glCgShaderContext_src.cxx

@@ -704,6 +704,12 @@ issue_parameters(int altered) {
       case Shader::SMP_cell15:
       case Shader::SMP_cell15:
         GLf(cgGLSetParameter1)(p, data[15]);
         GLf(cgGLSetParameter1)(p, data[15]);
         continue;
         continue;
+      case Shader::SMP_cell14:
+        GLf(cgGLSetParameter1)(p, data[14]);
+        continue;
+      case Shader::SMP_cell13:
+        GLf(cgGLSetParameter1)(p, data[13]);
+        continue;
       }
       }
     }
     }
   }
   }

+ 5 - 1
panda/src/glstuff/glGraphicsStateGuardian_src.cxx

@@ -2492,11 +2492,15 @@ reset() {
   if (is_at_least_gl_version(4, 1) || has_extension("GL_ARB_get_program_binary")) {
   if (is_at_least_gl_version(4, 1) || has_extension("GL_ARB_get_program_binary")) {
     _glGetProgramBinary = (PFNGLGETPROGRAMBINARYPROC)
     _glGetProgramBinary = (PFNGLGETPROGRAMBINARYPROC)
       get_extension_func("glGetProgramBinary");
       get_extension_func("glGetProgramBinary");
+    _glProgramBinary = (PFNGLPROGRAMBINARYPROC)
+      get_extension_func("glProgramBinary");
     _glProgramParameteri = (PFNGLPROGRAMPARAMETERIPROC)
     _glProgramParameteri = (PFNGLPROGRAMPARAMETERIPROC)
       get_extension_func("glProgramParameteri");
       get_extension_func("glProgramParameteri");
 
 
     GLint num_binary_formats = 0;
     GLint num_binary_formats = 0;
-    if (_glGetProgramBinary != NULL && _glProgramParameteri != NULL) {
+    if (_glGetProgramBinary != NULL &&
+        _glProgramBinary != NULL &&
+        _glProgramParameteri != NULL) {
       glGetIntegerv(GL_NUM_PROGRAM_BINARY_FORMATS, &num_binary_formats);
       glGetIntegerv(GL_NUM_PROGRAM_BINARY_FORMATS, &num_binary_formats);
     }
     }
 
 

+ 1 - 0
panda/src/glstuff/glGraphicsStateGuardian_src.h

@@ -940,6 +940,7 @@ public:
   PFNGLDISPATCHCOMPUTEPROC _glDispatchCompute;
   PFNGLDISPATCHCOMPUTEPROC _glDispatchCompute;
   PFNGLMEMORYBARRIERPROC _glMemoryBarrier;
   PFNGLMEMORYBARRIERPROC _glMemoryBarrier;
   PFNGLGETPROGRAMBINARYPROC _glGetProgramBinary;
   PFNGLGETPROGRAMBINARYPROC _glGetProgramBinary;
+  PFNGLPROGRAMBINARYPROC _glProgramBinary;
   PFNGLGETINTERNALFORMATIVPROC _glGetInternalformativ;
   PFNGLGETINTERNALFORMATIVPROC _glGetInternalformativ;
   PFNGLVIEWPORTARRAYVPROC _glViewportArrayv;
   PFNGLVIEWPORTARRAYVPROC _glViewportArrayv;
   PFNGLSCISSORARRAYVPROC _glScissorArrayv;
   PFNGLSCISSORARRAYVPROC _glScissorArrayv;

+ 61 - 21
panda/src/glstuff/glShaderContext_src.cxx

@@ -25,7 +25,7 @@
 #include "fogAttrib.h"
 #include "fogAttrib.h"
 #include "lightAttrib.h"
 #include "lightAttrib.h"
 #include "clipPlaneAttrib.h"
 #include "clipPlaneAttrib.h"
-#include "ambientLight.h"
+#include "bamCache.h"
 
 
 TypeHandle CLP(ShaderContext)::_type_handle;
 TypeHandle CLP(ShaderContext)::_type_handle;
 
 
@@ -2872,6 +2872,32 @@ glsl_compile_and_link() {
     _glgsg->_glObjectLabel(GL_PROGRAM, _glsl_program, name.size(), name.data());
     _glgsg->_glObjectLabel(GL_PROGRAM, _glsl_program, name.size(), name.data());
   }
   }
 
 
+#ifndef OPENGLES
+  // 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";
+    }
+  }
+#endif
+
   bool valid = true;
   bool valid = true;
 
 
   if (!_shader->get_text(Shader::ST_vertex).empty()) {
   if (!_shader->get_text(Shader::ST_vertex).empty()) {
@@ -2936,8 +2962,17 @@ glsl_compile_and_link() {
   }
   }
 
 
   // If we requested to retrieve the shader, we should indicate that before linking.
   // If we requested to retrieve the shader, we should indicate that before linking.
-#if !defined(NDEBUG) && !defined(OPENGLES)
-  if (gl_dump_compiled_shaders && _glgsg->_supports_get_program_binary) {
+#ifndef OPENGLES
+  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);
     _glgsg->_glProgramParameteri(_glsl_program, GL_PROGRAM_BINARY_RETRIEVABLE_HINT, GL_TRUE);
   }
   }
 #endif
 #endif
@@ -2961,33 +2996,38 @@ glsl_compile_and_link() {
   // Report any warnings.
   // Report any warnings.
   glsl_report_program_errors(_glsl_program, false);
   glsl_report_program_errors(_glsl_program, false);
 
 
-  // Dump the binary if requested.
-#if !defined(NDEBUG) && !defined(OPENGLES)
-  if (gl_dump_compiled_shaders && _glgsg->_supports_get_program_binary) {
+#ifndef OPENGLES
+  if (retrieve_binary) {
     GLint length = 0;
     GLint length = 0;
     _glgsg->_glGetProgramiv(_glsl_program, GL_PROGRAM_BINARY_LENGTH, &length);
     _glgsg->_glGetProgramiv(_glsl_program, GL_PROGRAM_BINARY_LENGTH, &length);
     length += 2;
     length += 2;
 
 
-    char filename[64];
-    static int gl_dump_count = 0;
-    sprintf(filename, "glsl_program%d.dump", gl_dump_count++);
-
-    char *binary = new char[length];
+    char *binary = (char *)alloca(length);
     GLenum format;
     GLenum format;
-    GLsizei num_bytes;
+    GLsizei num_bytes = 0;
     _glgsg->_glGetProgramBinary(_glsl_program, length, &num_bytes, &format, (void*)binary);
     _glgsg->_glGetProgramBinary(_glsl_program, length, &num_bytes, &format, (void*)binary);
 
 
-    pofstream s;
-    s.open(filename, ios::out | ios::binary | ios::trunc);
-    s.write(binary, num_bytes);
-    s.close();
+    _shader->set_compiled(format, binary, num_bytes);
 
 
-    GLCAT.info()
-      << "Dumped " << num_bytes << " bytes of program binary with format 0x"
-      << hex << format << dec << "  to " << filename << "\n";
-    delete[] binary;
-  }
+#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
 #endif  // NDEBUG
+  }
+#endif  // OPENGLES
 
 
   _glgsg->report_my_gl_errors();
   _glgsg->report_my_gl_errors();
   return true;
   return true;

+ 0 - 64
panda/src/gobj/config_gobj.cxx

@@ -362,23 +362,6 @@ ConfigVariableDouble simple_image_threshold
           "simple images.  Generally the value should be considerably "
           "simple images.  Generally the value should be considerably "
           "less than 1."));
           "less than 1."));
 
 
-ConfigVariableEnum<ShaderUtilization> shader_utilization
-("shader-utilization", SUT_none,
- PRC_DESC("At times, panda may generate shaders.  This variable controls what "
-          "kinds of shaders can be generated.  If you set it to SUT_none, "
-          "shader generation will be be disabled.  If you set it to SUT_basic, "
-          "then DX9 shaders may be generated, if you set it to SUT_advanced, "
-          "then DX10 shaders may be generated."));
-
-ConfigVariableBool shader_auto_utilization
-("shader-auto-utilization", false,
- PRC_DESC("If this is true, then panda will wait until you open a window, "
-          "and then ask the window if it supports basic or advanced shaders. "
-          "If so, then the config variable shader-utilization will "
-          "automatically be adusted.  The pitfall of doing this is that if "
-          "you then open a second window that doesn't support the same "
-          "capabilities, it will have no choice but to print an error message."));
-
 ConfigVariableInt geom_cache_size
 ConfigVariableInt geom_cache_size
 ("geom-cache-size", 5000,
 ("geom-cache-size", 5000,
  PRC_DESC("Specifies the maximum number of entries in the cache "
  PRC_DESC("Specifies the maximum number of entries in the cache "
@@ -651,50 +634,3 @@ ConfigureFn(config_gobj) {
   UserVertexSlider::register_with_read_factory();
   UserVertexSlider::register_with_read_factory();
   UserVertexTransform::register_with_read_factory();
   UserVertexTransform::register_with_read_factory();
 }
 }
-
-ostream &
-operator << (ostream &out, ShaderUtilization sgc) {
-  switch (sgc) {
-  case SUT_none:
-    return out << "none";
-
-  case SUT_basic:
-    return out << "basic";
-
-  case SUT_advanced:
-    return out << "advanced";
-
-  case SUT_unspecified:
-    return out << "unspecified";
-  }
-
-  return out << "**invalid ShaderUtilization (" << (int)sgc << ")**";
-}
-
-istream &
-operator >> (istream &in, ShaderUtilization &sgc) {
-  string word;
-  in >> word;
-
-  if (cmp_nocase(word, "none") == 0 ||
-      cmp_nocase(word, "0") == 0 ||
-      cmp_nocase(word, "#f") == 0 ||
-      (!word.empty() && tolower(word[0]) == 'f')) {
-    sgc = SUT_none;
-
-  } else if (cmp_nocase(word, "basic") == 0 ||
-             cmp_nocase(word, "1") == 0 ||
-             cmp_nocase(word, "#t") == 0 ||
-             (!word.empty() && tolower(word[0]) == 't')) {
-    sgc = SUT_basic;
-
-  } else if (cmp_nocase(word, "advanced") == 0) {
-    sgc = SUT_advanced;
-
-  } else {
-    gobj_cat->error() << "Invalid ShaderUtilization value: " << word << "\n";
-    sgc = SUT_none;
-  }
-
-  return in;
-}

+ 0 - 15
panda/src/gobj/config_gobj.h

@@ -29,18 +29,6 @@
 NotifyCategoryDecl(gobj, EXPCL_PANDA_GOBJ, EXPTP_PANDA_GOBJ);
 NotifyCategoryDecl(gobj, EXPCL_PANDA_GOBJ, EXPTP_PANDA_GOBJ);
 NotifyCategoryDecl(shader, EXPCL_PANDA_GOBJ, EXPTP_PANDA_GOBJ);
 NotifyCategoryDecl(shader, EXPCL_PANDA_GOBJ, EXPTP_PANDA_GOBJ);
 
 
-BEGIN_PUBLISH
-enum ShaderUtilization {
-  SUT_none,
-  SUT_basic,
-  SUT_advanced,
-  SUT_unspecified,
-};
-END_PUBLISH
-
-EXPCL_PANDA_GOBJ ostream &operator << (ostream &out, ShaderUtilization sut);
-EXPCL_PANDA_GOBJ istream &operator >> (istream &in, ShaderUtilization &sut);
-
 // Configure variables for gobj package.
 // Configure variables for gobj package.
 extern EXPCL_PANDA_GOBJ ConfigVariableInt max_texture_dimension;
 extern EXPCL_PANDA_GOBJ ConfigVariableInt max_texture_dimension;
 extern EXPCL_PANDA_GOBJ ConfigVariableDouble texture_scale;
 extern EXPCL_PANDA_GOBJ ConfigVariableDouble texture_scale;
@@ -77,9 +65,6 @@ extern EXPCL_PANDA_GOBJ ConfigVariableBool textures_header_only;
 extern EXPCL_PANDA_GOBJ ConfigVariableInt simple_image_size;
 extern EXPCL_PANDA_GOBJ ConfigVariableInt simple_image_size;
 extern EXPCL_PANDA_GOBJ ConfigVariableDouble simple_image_threshold;
 extern EXPCL_PANDA_GOBJ ConfigVariableDouble simple_image_threshold;
 
 
-extern EXPCL_PANDA_GOBJ ConfigVariableEnum<ShaderUtilization> shader_utilization;
-extern EXPCL_PANDA_GOBJ ConfigVariableBool shader_auto_utilization;
-
 extern EXPCL_PANDA_GOBJ ConfigVariableInt geom_cache_size;
 extern EXPCL_PANDA_GOBJ ConfigVariableInt geom_cache_size;
 extern EXPCL_PANDA_GOBJ ConfigVariableInt geom_cache_min_frames;
 extern EXPCL_PANDA_GOBJ ConfigVariableInt geom_cache_min_frames;
 extern EXPCL_PANDA_GOBJ ConfigVariableInt released_vbuffer_cache_size;
 extern EXPCL_PANDA_GOBJ ConfigVariableInt released_vbuffer_cache_size;

+ 48 - 37
panda/src/gobj/shader.I

@@ -102,57 +102,66 @@ get_error_flag() const {
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: Shader::set_shader_utilization
-//       Access: Published, Static
-//  Description: Set this flag to SUT_none, SUT_basic, or
-//               SUT_advanced to limit panda's automatic shader
-//               generation facilities.
+//     Function: Shader::get_language
+//       Access: Published
+//  Description: Returns the shader language in which this shader
+//               was written.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-INLINE void Shader::
-set_shader_utilization(ShaderUtilization sut) {
-  _shader_utilization = sut;
+INLINE Shader::ShaderLanguage Shader::
+get_language() const {
+  return _language;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: Shader::get_shader_utilization
-//       Access: Published, Static
-//  Description: This flag returns SUT_none, SUT_basic, or
-//               SUT_advanced and controls the automatic generation
-//               of shaders.  It is initialized from the config
-//               variable of the same name, but it can be
-//               subsequently adjusted.
+//     Function: Shader::has_fullpath
+//       Access: Published
+//  Description: Returns true if the fullpath has been set and
+//               is available.  See set_fullpath().
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-INLINE ShaderUtilization Shader::
-get_shader_utilization() {
-  if (_shader_utilization == SUT_unspecified) {
-    return shader_utilization;
-  } else {
-    return _shader_utilization;
-  }
+INLINE bool Shader::
+has_fullpath() const {
+  return !_fullpath.empty();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Shader::get_fullpath
+//       Access: Published
+//  Description: Returns the fullpath that has been set.  This is
+//               the full path to the file as it was found along the
+//               model-path.
+////////////////////////////////////////////////////////////////////
+INLINE const Filename &Shader::
+get_fullpath() const {
+  return _fullpath;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: Shader::have_shader_utilization
-//       Access: Published, Static
-//  Description: If true, then get_shader_utilization has been
-//               set using set_shader_utilization.
-//               If false, then get_shader_utilization simply
-//               returns the config variable of the same name.
+//     Function: Shader::get_cache_compiled_shader
+//       Access: Public
+//  Description: Returns the setting of the cache_compiled_shader
+//               flag.  See set_cache_compiled_shader().
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE bool Shader::
 INLINE bool Shader::
-have_shader_utilization() {
-  return (_shader_utilization != SUT_unspecified);
+get_cache_compiled_shader() const {
+  return _cache_compiled_shader;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: Shader::get_language
-//       Access: Published
-//  Description: Returns the shader language in which this shader
-//               was written.
+//     Function: Shader::set_cache_compiled_shader
+//       Access: Public
+//  Description: Sets the cache_compiled_shader flag.  When this is
+//               set, the next time the Shader is loaded on a GSG, it
+//               will automatically extract the compiled shader from
+//               the GSG and save it to the global BamCache.
+//
+//               This is used to store compiled shaders in the
+//               BamCache.  This flag should not be set explicitly; it
+//               is set automatically by the ShaderPool when
+//               model-cache-compiled-shaders is set true.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-INLINE Shader::ShaderLanguage Shader::
-get_language() const {
-  return _language;
+INLINE void Shader::
+set_cache_compiled_shader(bool flag) {
+  _cache_compiled_shader = flag;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -802,6 +811,7 @@ INLINE void Shader::ShaderFile::
 read_datagram(DatagramIterator &scan) {
 read_datagram(DatagramIterator &scan) {
   short count = scan.get_uint8();
   short count = scan.get_uint8();
   if (count > 0) {
   if (count > 0) {
+    _separate = true;
     if (count-- > 0) _vertex = scan.get_string();
     if (count-- > 0) _vertex = scan.get_string();
     if (count-- > 0) _fragment = scan.get_string();
     if (count-- > 0) _fragment = scan.get_string();
     if (count-- > 0) _geometry = scan.get_string();
     if (count-- > 0) _geometry = scan.get_string();
@@ -812,6 +822,7 @@ read_datagram(DatagramIterator &scan) {
       scan.get_string();
       scan.get_string();
     }
     }
   } else {
   } else {
+    _separate = false;
     _shared = scan.get_string();
     _shared = scan.get_string();
   }
   }
 }
 }

+ 95 - 19
panda/src/gobj/shader.cxx

@@ -18,6 +18,7 @@
 #include "preparedGraphicsObjects.h"
 #include "preparedGraphicsObjects.h"
 #include "virtualFileSystem.h"
 #include "virtualFileSystem.h"
 #include "config_util.h"
 #include "config_util.h"
+#include "bamCache.h"
 
 
 #ifdef HAVE_CG
 #ifdef HAVE_CG
 #include <Cg/cg.h>
 #include <Cg/cg.h>
@@ -28,7 +29,6 @@ Shader::ShaderTable Shader::_load_table;
 Shader::ShaderTable Shader::_make_table;
 Shader::ShaderTable Shader::_make_table;
 Shader::ShaderCaps Shader::_default_caps;
 Shader::ShaderCaps Shader::_default_caps;
 int Shader::_shaders_generated;
 int Shader::_shaders_generated;
-ShaderUtilization Shader::_shader_utilization = SUT_unspecified;
 
 
 #ifdef HAVE_CG
 #ifdef HAVE_CG
 CGcontext Shader::_cg_context = 0;
 CGcontext Shader::_cg_context = 0;
@@ -1460,7 +1460,6 @@ compile_parameter(ShaderArgInfo &p, int *arg_dim) {
   return false;
   return false;
 }
 }
 
 
-
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: Shader::clear_parameters
 //     Function: Shader::clear_parameters
 //       Access: Private
 //       Access: Private
@@ -1473,6 +1472,38 @@ clear_parameters() {
   _tex_spec.clear();
   _tex_spec.clear();
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: Shader::set_compiled
+//       Access: Private
+//  Description: Called by the back-end when the shader has compiled
+//               data available.
+////////////////////////////////////////////////////////////////////
+void Shader::
+set_compiled(unsigned int format, const char *data, size_t length) {
+  _compiled_format = format;
+  _compiled_binary.assign(data, length);
+
+  // Store the compiled shader in the cache.
+  if (_cache_compiled_shader && !_record.is_null()) {
+    _record->set_data(this);
+
+    BamCache *cache = BamCache::get_global_ptr();
+    cache->store(_record);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Shader::get_compiled
+//       Access: Private
+//  Description: Called by the back-end to retrieve compiled data.
+////////////////////////////////////////////////////////////////////
+bool Shader::
+get_compiled(unsigned int &format, string &binary) const {
+  format = _compiled_format;
+  binary = _compiled_binary;
+  return !binary.empty();
+}
+
 #ifdef HAVE_CG
 #ifdef HAVE_CG
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: Shader::cg_parameter_type
 //     Function: Shader::cg_parameter_type
@@ -2204,7 +2235,8 @@ Shader(ShaderLanguage lang) :
   _loaded(false),
   _loaded(false),
   _language(lang),
   _language(lang),
   _last_modified(0),
   _last_modified(0),
-  _mat_deps(0)
+  _mat_deps(0),
+  _cache_compiled_shader(false)
 {
 {
 #ifdef HAVE_CG
 #ifdef HAVE_CG
   _cg_vprogram = 0;
   _cg_vprogram = 0;
@@ -2234,7 +2266,7 @@ Shader(ShaderLanguage lang) :
 //               Returns a boolean indicating success or failure.
 //               Returns a boolean indicating success or failure.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 bool Shader::
 bool Shader::
-read(const ShaderFile &sfile) {
+read(const ShaderFile &sfile, BamCacheRecord *record) {
   _text._separate = sfile._separate;
   _text._separate = sfile._separate;
 
 
   if (sfile._separate) {
   if (sfile._separate) {
@@ -2244,30 +2276,37 @@ read(const ShaderFile &sfile) {
       return false;
       return false;
     }
     }
 
 
-    if (!sfile._vertex.empty() && !do_read_source(_text._vertex, sfile._vertex)) {
+    if (!sfile._vertex.empty() &&
+        !do_read_source(_text._vertex, sfile._vertex, record)) {
       return false;
       return false;
     }
     }
-    if (!sfile._fragment.empty() && !do_read_source(_text._fragment, sfile._fragment)) {
+    if (!sfile._fragment.empty() &&
+        !do_read_source(_text._fragment, sfile._fragment, record)) {
       return false;
       return false;
     }
     }
-    if (!sfile._geometry.empty() && !do_read_source(_text._geometry, sfile._geometry)) {
+    if (!sfile._geometry.empty() &&
+        !do_read_source(_text._geometry, sfile._geometry, record)) {
       return false;
       return false;
     }
     }
-    if (!sfile._tess_control.empty() && !do_read_source(_text._tess_control, sfile._tess_control)) {
+    if (!sfile._tess_control.empty() &&
+        !do_read_source(_text._tess_control, sfile._tess_control, record)) {
       return false;
       return false;
     }
     }
-    if (!sfile._tess_evaluation.empty() && !do_read_source(_text._tess_evaluation, sfile._tess_evaluation)) {
+    if (!sfile._tess_evaluation.empty() &&
+        !do_read_source(_text._tess_evaluation, sfile._tess_evaluation, record)) {
       return false;
       return false;
     }
     }
-    if (!sfile._compute.empty() && !do_read_source(_text._compute, sfile._compute)) {
+    if (!sfile._compute.empty() &&
+        !do_read_source(_text._compute, sfile._compute, record)) {
       return false;
       return false;
     }
     }
     _filename = sfile;
     _filename = sfile;
 
 
   } else {
   } else {
-    if (!do_read_source(_text._shared, sfile._shared)) {
+    if (!do_read_source(_text._shared, sfile._shared, record)) {
       return false;
       return false;
     }
     }
+    _fullpath = _source_files[0];
     _filename = sfile;
     _filename = sfile;
 
 
     // Determine which language the shader is written in.
     // Determine which language the shader is written in.
@@ -2323,12 +2362,12 @@ read(const ShaderFile &sfile) {
 //               bad enough to consider it 'invalid'.
 //               bad enough to consider it 'invalid'.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 bool Shader::
 bool Shader::
-do_read_source(string &into, const Filename &fn) {
+do_read_source(string &into, const Filename &fn, BamCacheRecord *record) {
   if (_language == SL_GLSL && glsl_preprocess) {
   if (_language == SL_GLSL && glsl_preprocess) {
     // Preprocess the GLSL file as we read it.
     // Preprocess the GLSL file as we read it.
     set<Filename> open_files;
     set<Filename> open_files;
     ostringstream sstr;
     ostringstream sstr;
-    if (!r_preprocess_source(sstr, fn, Filename(), open_files)) {
+    if (!r_preprocess_source(sstr, fn, Filename(), open_files, record)) {
       return false;
       return false;
     }
     }
     into = sstr.str();
     into = sstr.str();
@@ -2350,6 +2389,9 @@ do_read_source(string &into, const Filename &fn) {
       return false;
       return false;
     }
     }
 
 
+    if (record != (BamCacheRecord *)NULL) {
+      record->add_dependent_file(vf);
+    }
     _last_modified = max(_last_modified, vf->get_timestamp());
     _last_modified = max(_last_modified, vf->get_timestamp());
     _source_files.push_back(vf->get_filename());
     _source_files.push_back(vf->get_filename());
   }
   }
@@ -2368,7 +2410,8 @@ do_read_source(string &into, const Filename &fn) {
 bool Shader::
 bool Shader::
 r_preprocess_source(ostream &out, const Filename &fn,
 r_preprocess_source(ostream &out, const Filename &fn,
                     const Filename &source_dir,
                     const Filename &source_dir,
-                    set<Filename> &once_files, int depth) {
+                    set<Filename> &once_files,
+                    BamCacheRecord *record, int depth) {
 
 
   if (depth > glsl_include_recursion_limit) {
   if (depth > glsl_include_recursion_limit) {
     shader_cat.error()
     shader_cat.error()
@@ -2402,6 +2445,9 @@ r_preprocess_source(ostream &out, const Filename &fn,
     return false;
     return false;
   }
   }
 
 
+  if (record != (BamCacheRecord *)NULL) {
+    record->add_dependent_file(vf);
+  }
   _last_modified = max(_last_modified, vf->get_timestamp());
   _last_modified = max(_last_modified, vf->get_timestamp());
   _source_files.push_back(full_fn);
   _source_files.push_back(full_fn);
 
 
@@ -2483,7 +2529,7 @@ r_preprocess_source(ostream &out, const Filename &fn,
       }
       }
 
 
       // OK, great.  Process the include.
       // OK, great.  Process the include.
-      if (!r_preprocess_source(out, incfn, source_dir, once_files, depth + 1)) {
+      if (!r_preprocess_source(out, incfn, source_dir, once_files, record, depth + 1)) {
         // An error occurred.  Pass on the failure.
         // An error occurred.  Pass on the failure.
         shader_cat.error(false) << "included at line "
         shader_cat.error(false) << "included at line "
           << lineno << " of file " << fn << ":\n  " << line << "\n";
           << lineno << " of file " << fn << ":\n  " << line << "\n";
@@ -2768,6 +2814,14 @@ load_compute(ShaderLanguage lang, const Filename &fn) {
     return NULL;
     return NULL;
   }
   }
 
 
+  Filename fullpath(fn);
+  VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
+  if (!vfs->resolve_filename(fullpath, get_model_path())) {
+    shader_cat.error()
+      << "Could not find compute shader file: " << fn << "\n";
+    return NULL;
+  }
+
   ShaderFile sfile;
   ShaderFile sfile;
   sfile._separate = true;
   sfile._separate = true;
   sfile._compute = fn;
   sfile._compute = fn;
@@ -2785,11 +2839,29 @@ load_compute(ShaderLanguage lang, const Filename &fn) {
     }
     }
   }
   }
 
 
+  BamCache *cache = BamCache::get_global_ptr();
+  PT(BamCacheRecord) record = cache->lookup(fullpath, "sho");
+  if (record != (BamCacheRecord *)NULL) {
+    if (record->has_data()) {
+      shader_cat.info()
+        << "Compute shader " << fn << " was found in disk cache.\n";
+
+      return DCAST(Shader, record->get_data());
+    }
+  }
+
   PT(Shader) shader = new Shader(lang);
   PT(Shader) shader = new Shader(lang);
-  if (!shader->read(sfile)) {
+
+  if (!shader->read(sfile, record)) {
     return NULL;
     return NULL;
   }
   }
 
 
+  // It makes little sense to cache the shader before compilation, so
+  // we keep the record for when we have the compiled the shader.
+  swap(shader->_record, record);
+  shader->_cache_compiled_shader = BamCache::get_global_ptr()->get_cache_compiled_shaders();
+  shader->_fullpath = shader->_source_files[0];
+
   _load_table[sfile] = shader;
   _load_table[sfile] = shader;
   return shader;
   return shader;
 }
 }
@@ -3195,11 +3267,9 @@ clear() {
   _active_vprofile = CG_PROFILE_UNKNOWN;
   _active_vprofile = CG_PROFILE_UNKNOWN;
   _active_fprofile = CG_PROFILE_UNKNOWN;
   _active_fprofile = CG_PROFILE_UNKNOWN;
   _active_gprofile = CG_PROFILE_UNKNOWN;
   _active_gprofile = CG_PROFILE_UNKNOWN;
-  _active_fprofile = CG_PROFILE_UNKNOWN;
   _ultimate_vprofile = CG_PROFILE_UNKNOWN;
   _ultimate_vprofile = CG_PROFILE_UNKNOWN;
   _ultimate_fprofile = CG_PROFILE_UNKNOWN;
   _ultimate_fprofile = CG_PROFILE_UNKNOWN;
   _ultimate_gprofile = CG_PROFILE_UNKNOWN;
   _ultimate_gprofile = CG_PROFILE_UNKNOWN;
-  _ultimate_fprofile = CG_PROFILE_UNKNOWN;
 #endif
 #endif
 }
 }
 
 
@@ -3211,7 +3281,7 @@ clear() {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void Shader::
 void Shader::
 register_with_read_factory() {
 register_with_read_factory() {
-  //BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
+  BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -3226,6 +3296,9 @@ write_datagram(BamWriter *manager, Datagram &dg) {
   dg.add_bool(_loaded);
   dg.add_bool(_loaded);
   _filename.write_datagram(dg);
   _filename.write_datagram(dg);
   _text.write_datagram(dg);
   _text.write_datagram(dg);
+
+  dg.add_uint32(_compiled_format);
+  dg.add_string(_compiled_binary);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -3260,4 +3333,7 @@ fillin(DatagramIterator &scan, BamReader *manager) {
   _loaded = scan.get_bool();
   _loaded = scan.get_bool();
   _filename.read_datagram(scan);
   _filename.read_datagram(scan);
   _text.read_datagram(scan);
   _text.read_datagram(scan);
+
+  _compiled_format = scan.get_uint32();
+  _compiled_binary = scan.get_string();
 }
 }

+ 24 - 9
panda/src/gobj/shader.h

@@ -40,6 +40,8 @@ typedef struct _CGprogram   *CGprogram;
 typedef struct _CGparameter *CGparameter;
 typedef struct _CGparameter *CGparameter;
 #endif
 #endif
 
 
+class BamCacheRecord;
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //       Class : Shader
 //       Class : Shader
 //      Summary: The Shader class is meant to select the Shader Language,
 //      Summary: The Shader class is meant to select the Shader Language,
@@ -103,9 +105,11 @@ PUBLISHED:
   INLINE bool get_error_flag() const;
   INLINE bool get_error_flag() const;
   INLINE ShaderLanguage get_language() const;
   INLINE ShaderLanguage get_language() const;
 
 
-  INLINE static ShaderUtilization get_shader_utilization();
-  INLINE static void set_shader_utilization(ShaderUtilization utl);
-  INLINE static bool have_shader_utilization();
+  INLINE bool has_fullpath() const;
+  INLINE const Filename &get_fullpath() const;
+
+  INLINE bool get_cache_compiled_shader() const;
+  INLINE void set_cache_compiled_shader(bool flag);
 
 
   void prepare(PreparedGraphicsObjects *prepared_objects);
   void prepare(PreparedGraphicsObjects *prepared_objects);
   bool is_prepared(PreparedGraphicsObjects *prepared_objects) const;
   bool is_prepared(PreparedGraphicsObjects *prepared_objects) const;
@@ -507,6 +511,9 @@ public:
 
 
   void clear_parameters();
   void clear_parameters();
 
 
+  void set_compiled(unsigned int format, const char *data, size_t length);
+  bool get_compiled(unsigned int &format, string &binary) const;
+
 private:
 private:
 #ifdef HAVE_CG
 #ifdef HAVE_CG
   ShaderArgClass cg_parameter_class(CGparameter p);
   ShaderArgClass cg_parameter_class(CGparameter p);
@@ -557,18 +564,25 @@ public:
 
 
 protected:
 protected:
   ShaderFile _filename;
   ShaderFile _filename;
+  Filename _fullpath;
   int _parse;
   int _parse;
   bool _loaded;
   bool _loaded;
   ShaderLanguage _language;
   ShaderLanguage _language;
-  pvector<Filename> _included_files;
+
+  typedef pvector<Filename> Filenames;
+  Filenames _included_files;
 
 
   // Stores full paths, and includes the fullpaths of the shaders
   // Stores full paths, and includes the fullpaths of the shaders
   // themselves as well as the includes.
   // themselves as well as the includes.
-  pvector<Filename> _source_files;
+  Filenames _source_files;
   time_t _last_modified;
   time_t _last_modified;
 
 
+  PT(BamCacheRecord) _record;
+  bool _cache_compiled_shader;
+  unsigned int _compiled_format;
+  string _compiled_binary;
+
   static ShaderCaps _default_caps;
   static ShaderCaps _default_caps;
-  static ShaderUtilization _shader_utilization;
   static int _shaders_generated;
   static int _shaders_generated;
 
 
   typedef pmap<ShaderFile, PT(Shader)> ShaderTable;
   typedef pmap<ShaderFile, PT(Shader)> ShaderTable;
@@ -587,11 +601,12 @@ private:
 
 
   Shader(ShaderLanguage lang);
   Shader(ShaderLanguage lang);
 
 
-  bool read(const ShaderFile &sfile);
-  bool do_read_source(string &into, const Filename &fn);
+  bool read(const ShaderFile &sfile, BamCacheRecord *record = NULL);
+  bool do_read_source(string &into, const Filename &fn, BamCacheRecord *record);
   bool r_preprocess_source(ostream &out, const Filename &fn,
   bool r_preprocess_source(ostream &out, const Filename &fn,
                            const Filename &source_dir,
                            const Filename &source_dir,
-                           set<Filename> &open_files, int depth = 0);
+                           set<Filename> &open_files,
+                           BamCacheRecord *record, int depth = 0);
 
 
   bool check_modified() const;
   bool check_modified() const;
 
 

+ 0 - 1
panda/src/pgraph/config_pgraph.cxx

@@ -504,7 +504,6 @@ init_libpgraph() {
   ShadeModelAttrib::register_with_read_factory();
   ShadeModelAttrib::register_with_read_factory();
   ShaderInput::register_with_read_factory();
   ShaderInput::register_with_read_factory();
   ShaderAttrib::register_with_read_factory();
   ShaderAttrib::register_with_read_factory();
-  Shader::register_with_read_factory();
   ShowBoundsEffect::register_with_read_factory();
   ShowBoundsEffect::register_with_read_factory();
   TexMatrixAttrib::register_with_read_factory();
   TexMatrixAttrib::register_with_read_factory();
   TexProjectorEffect::register_with_read_factory();
   TexProjectorEffect::register_with_read_factory();

+ 29 - 0
panda/src/putil/bamCache.I

@@ -142,6 +142,35 @@ get_cache_compressed_textures() const {
   return _cache_compressed_textures && _active;
   return _cache_compressed_textures && _active;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: BamCache::set_cache_compiled_shaders
+//       Access: Published
+//  Description: Indicates whether compiled shader programs will be
+//               stored in the cache, as binary .sho files.  This
+//               may not be supported by all shader languages or
+//               graphics renderers.
+////////////////////////////////////////////////////////////////////
+INLINE void BamCache::
+set_cache_compiled_shaders(bool flag) {
+  ReMutexHolder holder(_lock);
+  _cache_compiled_shaders = flag;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BamCache::get_cache_compiled_shaders
+//       Access: Published
+//  Description: Returns whether compiled shader programs will be
+//               stored in the cache, as binary .txo files.  See
+//               set_cache_compiled_shaders().
+//
+//               This also returns false if get_active() is false.
+////////////////////////////////////////////////////////////////////
+INLINE bool BamCache::
+get_cache_compiled_shaders() const {
+  ReMutexHolder holder(_lock);
+  return _cache_compiled_shaders && _active;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: BamCache::get_root
 //     Function: BamCache::get_root
 //       Access: Published
 //       Access: Published

+ 7 - 0
panda/src/putil/bamCache.cxx

@@ -74,6 +74,12 @@ BamCache() :
               "by the GSG.  This may be set in conjunction with "
               "by the GSG.  This may be set in conjunction with "
               "model-cache-textures, or it may be independent."));
               "model-cache-textures, or it may be independent."));
 
 
+  ConfigVariableBool model_cache_compiled_shaders
+    ("model-cache-compiled-shaders", false,
+     PRC_DESC("If this is set to true, compiled shaders will be cached "
+              "in the model cache, in their binary form as downloaded "
+              "by the GSG."));
+
   ConfigVariableInt model_cache_max_kbytes
   ConfigVariableInt model_cache_max_kbytes
     ("model-cache-max-kbytes", 10485760,
     ("model-cache-max-kbytes", 10485760,
      PRC_DESC("This is the maximum size of the model cache, in kilobytes."));
      PRC_DESC("This is the maximum size of the model cache, in kilobytes."));
@@ -81,6 +87,7 @@ BamCache() :
   _cache_models = model_cache_models;
   _cache_models = model_cache_models;
   _cache_textures = model_cache_textures;
   _cache_textures = model_cache_textures;
   _cache_compressed_textures = model_cache_compressed_textures;
   _cache_compressed_textures = model_cache_compressed_textures;
+  _cache_compiled_shaders = model_cache_compiled_shaders;
 
 
   _flush_time = model_cache_flush;
   _flush_time = model_cache_flush;
   _max_kbytes = model_cache_max_kbytes;
   _max_kbytes = model_cache_max_kbytes;

+ 6 - 0
panda/src/putil/bamCache.h

@@ -61,6 +61,9 @@ PUBLISHED:
   INLINE void set_cache_compressed_textures(bool flag);
   INLINE void set_cache_compressed_textures(bool flag);
   INLINE bool get_cache_compressed_textures() const;
   INLINE bool get_cache_compressed_textures() const;
 
 
+  INLINE void set_cache_compiled_shaders(bool flag);
+  INLINE bool get_cache_compiled_shaders() const;
+
   void set_root(const Filename &root);
   void set_root(const Filename &root);
   INLINE Filename get_root() const;
   INLINE Filename get_root() const;
 
 
@@ -92,6 +95,8 @@ PUBLISHED:
   MAKE_PROPERTY(cache_textures, get_cache_textures, set_cache_textures);
   MAKE_PROPERTY(cache_textures, get_cache_textures, set_cache_textures);
   MAKE_PROPERTY(cache_compressed_textures, get_cache_compressed_textures,
   MAKE_PROPERTY(cache_compressed_textures, get_cache_compressed_textures,
                                            set_cache_compressed_textures);
                                            set_cache_compressed_textures);
+  MAKE_PROPERTY(cache_compiled_shaders, get_cache_compiled_shaders,
+                                        set_cache_compiled_shaders);
   MAKE_PROPERTY(root, get_root, set_root);
   MAKE_PROPERTY(root, get_root, set_root);
   MAKE_PROPERTY(flush_time, get_flush_time, set_flush_time);
   MAKE_PROPERTY(flush_time, get_flush_time, set_flush_time);
   MAKE_PROPERTY(cache_max_kbytes, get_cache_max_kbytes, set_cache_max_kbytes);
   MAKE_PROPERTY(cache_max_kbytes, get_cache_max_kbytes, set_cache_max_kbytes);
@@ -130,6 +135,7 @@ private:
   bool _cache_models;
   bool _cache_models;
   bool _cache_textures;
   bool _cache_textures;
   bool _cache_compressed_textures;
   bool _cache_compressed_textures;
+  bool _cache_compiled_shaders;
   bool _read_only;
   bool _read_only;
   Filename _root;
   Filename _root;
   int _flush_time;
   int _flush_time;

+ 21 - 0
panda/src/putil/bamCacheRecord.cxx

@@ -188,6 +188,27 @@ add_dependent_file(const Filename &pathname) {
   }
   }
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: BamCacheRecord::add_dependent_file
+//       Access: Published
+//  Description: Variant of add_dependent_file that takes an already
+//               opened VirtualFile.
+////////////////////////////////////////////////////////////////////
+void BamCacheRecord::
+add_dependent_file(const VirtualFile *file) {
+  _files.push_back(DependentFile());
+  DependentFile &dfile = _files.back();
+  dfile._pathname = file->get_filename();
+  dfile._pathname.make_absolute();
+
+  dfile._timestamp = file->get_timestamp();
+  dfile._size = file->get_file_size();
+
+  if (dfile._pathname == _source_pathname) {
+    _source_timestamp = dfile._timestamp;
+  }
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: BamCacheRecord::output
 //     Function: BamCacheRecord::output
 //       Access: Published
 //       Access: Published

+ 2 - 0
panda/src/putil/bamCacheRecord.h

@@ -26,6 +26,7 @@ class Datagram;
 class DatagramIterator;
 class DatagramIterator;
 class FactoryParams;
 class FactoryParams;
 class BamCacheRecord;
 class BamCacheRecord;
+class VirtualFile;
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //       Class : BamCacheRecord
 //       Class : BamCacheRecord
@@ -66,6 +67,7 @@ PUBLISHED:
   bool dependents_unchanged() const;
   bool dependents_unchanged() const;
   void clear_dependent_files();
   void clear_dependent_files();
   void add_dependent_file(const Filename &pathname);
   void add_dependent_file(const Filename &pathname);
+  void add_dependent_file(const VirtualFile *file);
 
 
   INLINE bool has_data() const;
   INLINE bool has_data() const;
   INLINE void clear_data();
   INLINE void clear_data();