From 6eb460c3592f28d2f523681477c2f20e75ab6b23 Mon Sep 17 00:00:00 2001 From: rdb Date: Wed, 13 Jan 2016 19:58:11 +0100 Subject: [PATCH] Add ability to cache compiled GLSL shaders, remove unused ShaderUtilization --- panda/src/display/graphicsEngine.cxx | 46 +------ panda/src/glstuff/glCgShaderContext_src.cxx | 6 + .../glstuff/glGraphicsStateGuardian_src.cxx | 6 +- .../src/glstuff/glGraphicsStateGuardian_src.h | 1 + panda/src/glstuff/glShaderContext_src.cxx | 82 +++++++++---- panda/src/gobj/config_gobj.cxx | 64 ---------- panda/src/gobj/config_gobj.h | 15 --- panda/src/gobj/shader.I | 97 ++++++++------- panda/src/gobj/shader.cxx | 114 +++++++++++++++--- panda/src/gobj/shader.h | 33 +++-- panda/src/pgraph/config_pgraph.cxx | 1 - panda/src/putil/bamCache.I | 29 +++++ panda/src/putil/bamCache.cxx | 7 ++ panda/src/putil/bamCache.h | 6 + panda/src/putil/bamCacheRecord.cxx | 21 ++++ panda/src/putil/bamCacheRecord.h | 2 + 16 files changed, 313 insertions(+), 217 deletions(-) diff --git a/panda/src/display/graphicsEngine.cxx b/panda/src/display/graphicsEngine.cxx index 71ad115e8c..fa95aff9d9 100644 --- a/panda/src/display/graphicsEngine.cxx +++ b/panda/src/display/graphicsEngine.cxx @@ -1152,6 +1152,8 @@ extract_texture_data(Texture *tex, GraphicsStateGuardian *gsg) { //////////////////////////////////////////////////////////////////// void GraphicsEngine:: dispatch_compute(const LVecBase3i &work_groups, const ShaderAttrib *sattr, GraphicsStateGuardian *gsg) { + nassertv(sattr->get_shader() != (Shader *)NULL); + ReMutexHolder holder(_lock); 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"; - } - } - } } //////////////////////////////////////////////////////////////////// diff --git a/panda/src/glstuff/glCgShaderContext_src.cxx b/panda/src/glstuff/glCgShaderContext_src.cxx index 350a40b335..bbc05960ac 100644 --- a/panda/src/glstuff/glCgShaderContext_src.cxx +++ b/panda/src/glstuff/glCgShaderContext_src.cxx @@ -704,6 +704,12 @@ issue_parameters(int altered) { case Shader::SMP_cell15: GLf(cgGLSetParameter1)(p, data[15]); continue; + case Shader::SMP_cell14: + GLf(cgGLSetParameter1)(p, data[14]); + continue; + case Shader::SMP_cell13: + GLf(cgGLSetParameter1)(p, data[13]); + continue; } } } diff --git a/panda/src/glstuff/glGraphicsStateGuardian_src.cxx b/panda/src/glstuff/glGraphicsStateGuardian_src.cxx index 69a3f2cbab..b0d2decf8b 100644 --- a/panda/src/glstuff/glGraphicsStateGuardian_src.cxx +++ b/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")) { _glGetProgramBinary = (PFNGLGETPROGRAMBINARYPROC) get_extension_func("glGetProgramBinary"); + _glProgramBinary = (PFNGLPROGRAMBINARYPROC) + get_extension_func("glProgramBinary"); _glProgramParameteri = (PFNGLPROGRAMPARAMETERIPROC) get_extension_func("glProgramParameteri"); 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); } diff --git a/panda/src/glstuff/glGraphicsStateGuardian_src.h b/panda/src/glstuff/glGraphicsStateGuardian_src.h index 894dd05439..846fc482e5 100644 --- a/panda/src/glstuff/glGraphicsStateGuardian_src.h +++ b/panda/src/glstuff/glGraphicsStateGuardian_src.h @@ -940,6 +940,7 @@ public: PFNGLDISPATCHCOMPUTEPROC _glDispatchCompute; PFNGLMEMORYBARRIERPROC _glMemoryBarrier; PFNGLGETPROGRAMBINARYPROC _glGetProgramBinary; + PFNGLPROGRAMBINARYPROC _glProgramBinary; PFNGLGETINTERNALFORMATIVPROC _glGetInternalformativ; PFNGLVIEWPORTARRAYVPROC _glViewportArrayv; PFNGLSCISSORARRAYVPROC _glScissorArrayv; diff --git a/panda/src/glstuff/glShaderContext_src.cxx b/panda/src/glstuff/glShaderContext_src.cxx index bbc49412f4..7e4f973381 100644 --- a/panda/src/glstuff/glShaderContext_src.cxx +++ b/panda/src/glstuff/glShaderContext_src.cxx @@ -25,7 +25,7 @@ #include "fogAttrib.h" #include "lightAttrib.h" #include "clipPlaneAttrib.h" -#include "ambientLight.h" +#include "bamCache.h" TypeHandle CLP(ShaderContext)::_type_handle; @@ -2872,6 +2872,32 @@ glsl_compile_and_link() { _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; 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 !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); } #endif @@ -2961,33 +2996,38 @@ glsl_compile_and_link() { // Report any warnings. glsl_report_program_errors(_glsl_program, false); - // Dump the binary if requested. -#if !defined(NDEBUG) && !defined(OPENGLES) - if (gl_dump_compiled_shaders && _glgsg->_supports_get_program_binary) { +#ifndef OPENGLES + if (retrieve_binary) { GLint length = 0; _glgsg->_glGetProgramiv(_glsl_program, GL_PROGRAM_BINARY_LENGTH, &length); length += 2; - char filename[64]; - static int gl_dump_count = 0; - sprintf(filename, "glsl_program%d.dump", gl_dump_count++); - - char *binary = new char[length]; + char *binary = (char *)alloca(length); GLenum format; - GLsizei num_bytes; + GLsizei num_bytes = 0; _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 // OPENGLES _glgsg->report_my_gl_errors(); return true; diff --git a/panda/src/gobj/config_gobj.cxx b/panda/src/gobj/config_gobj.cxx index 7723d6b10b..0481a63dc1 100644 --- a/panda/src/gobj/config_gobj.cxx +++ b/panda/src/gobj/config_gobj.cxx @@ -362,23 +362,6 @@ ConfigVariableDouble simple_image_threshold "simple images. Generally the value should be considerably " "less than 1.")); -ConfigVariableEnum 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 ("geom-cache-size", 5000, PRC_DESC("Specifies the maximum number of entries in the cache " @@ -651,50 +634,3 @@ ConfigureFn(config_gobj) { UserVertexSlider::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; -} diff --git a/panda/src/gobj/config_gobj.h b/panda/src/gobj/config_gobj.h index b7634f0480..de9e14050d 100644 --- a/panda/src/gobj/config_gobj.h +++ b/panda/src/gobj/config_gobj.h @@ -29,18 +29,6 @@ NotifyCategoryDecl(gobj, 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. extern EXPCL_PANDA_GOBJ ConfigVariableInt max_texture_dimension; 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 ConfigVariableDouble simple_image_threshold; -extern EXPCL_PANDA_GOBJ ConfigVariableEnum 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_min_frames; extern EXPCL_PANDA_GOBJ ConfigVariableInt released_vbuffer_cache_size; diff --git a/panda/src/gobj/shader.I b/panda/src/gobj/shader.I index f24f9797ca..4ab9611819 100644 --- a/panda/src/gobj/shader.I +++ b/panda/src/gobj/shader.I @@ -101,49 +101,6 @@ get_error_flag() const { return _error_flag; } -//////////////////////////////////////////////////////////////////// -// 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. -//////////////////////////////////////////////////////////////////// -INLINE void Shader:: -set_shader_utilization(ShaderUtilization sut) { - _shader_utilization = sut; -} - -//////////////////////////////////////////////////////////////////// -// 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. -//////////////////////////////////////////////////////////////////// -INLINE ShaderUtilization Shader:: -get_shader_utilization() { - if (_shader_utilization == SUT_unspecified) { - return shader_utilization; - } else { - return _shader_utilization; - } -} - -//////////////////////////////////////////////////////////////////// -// 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. -//////////////////////////////////////////////////////////////////// -INLINE bool Shader:: -have_shader_utilization() { - return (_shader_utilization != SUT_unspecified); -} - //////////////////////////////////////////////////////////////////// // Function: Shader::get_language // Access: Published @@ -155,6 +112,58 @@ get_language() const { return _language; } +//////////////////////////////////////////////////////////////////// +// Function: Shader::has_fullpath +// Access: Published +// Description: Returns true if the fullpath has been set and +// is available. See set_fullpath(). +//////////////////////////////////////////////////////////////////// +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::get_cache_compiled_shader +// Access: Public +// Description: Returns the setting of the cache_compiled_shader +// flag. See set_cache_compiled_shader(). +//////////////////////////////////////////////////////////////////// +INLINE bool Shader:: +get_cache_compiled_shader() const { + return _cache_compiled_shader; +} + +//////////////////////////////////////////////////////////////////// +// 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 void Shader:: +set_cache_compiled_shader(bool flag) { + _cache_compiled_shader = flag; +} + //////////////////////////////////////////////////////////////////// // Function: Shader::ShaderCapabilities Constructor // Access: Public @@ -802,6 +811,7 @@ INLINE void Shader::ShaderFile:: read_datagram(DatagramIterator &scan) { short count = scan.get_uint8(); if (count > 0) { + _separate = true; if (count-- > 0) _vertex = scan.get_string(); if (count-- > 0) _fragment = scan.get_string(); if (count-- > 0) _geometry = scan.get_string(); @@ -812,6 +822,7 @@ read_datagram(DatagramIterator &scan) { scan.get_string(); } } else { + _separate = false; _shared = scan.get_string(); } } diff --git a/panda/src/gobj/shader.cxx b/panda/src/gobj/shader.cxx index 2ee015ccbc..e23f8a950a 100644 --- a/panda/src/gobj/shader.cxx +++ b/panda/src/gobj/shader.cxx @@ -18,6 +18,7 @@ #include "preparedGraphicsObjects.h" #include "virtualFileSystem.h" #include "config_util.h" +#include "bamCache.h" #ifdef HAVE_CG #include @@ -28,7 +29,6 @@ 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; #ifdef HAVE_CG CGcontext Shader::_cg_context = 0; @@ -1460,7 +1460,6 @@ compile_parameter(ShaderArgInfo &p, int *arg_dim) { return false; } - //////////////////////////////////////////////////////////////////// // Function: Shader::clear_parameters // Access: Private @@ -1473,6 +1472,38 @@ clear_parameters() { _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 //////////////////////////////////////////////////////////////////// // Function: Shader::cg_parameter_type @@ -2204,7 +2235,8 @@ Shader(ShaderLanguage lang) : _loaded(false), _language(lang), _last_modified(0), - _mat_deps(0) + _mat_deps(0), + _cache_compiled_shader(false) { #ifdef HAVE_CG _cg_vprogram = 0; @@ -2234,7 +2266,7 @@ Shader(ShaderLanguage lang) : // Returns a boolean indicating success or failure. //////////////////////////////////////////////////////////////////// bool Shader:: -read(const ShaderFile &sfile) { +read(const ShaderFile &sfile, BamCacheRecord *record) { _text._separate = sfile._separate; if (sfile._separate) { @@ -2244,30 +2276,37 @@ read(const ShaderFile &sfile) { 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; } - 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; } - 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; } - 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; } - 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; } - 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; } _filename = sfile; } else { - if (!do_read_source(_text._shared, sfile._shared)) { + if (!do_read_source(_text._shared, sfile._shared, record)) { return false; } + _fullpath = _source_files[0]; _filename = sfile; // Determine which language the shader is written in. @@ -2323,12 +2362,12 @@ read(const ShaderFile &sfile) { // bad enough to consider it 'invalid'. //////////////////////////////////////////////////////////////////// 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) { // Preprocess the GLSL file as we read it. set open_files; ostringstream sstr; - if (!r_preprocess_source(sstr, fn, Filename(), open_files)) { + if (!r_preprocess_source(sstr, fn, Filename(), open_files, record)) { return false; } into = sstr.str(); @@ -2350,6 +2389,9 @@ do_read_source(string &into, const Filename &fn) { return false; } + if (record != (BamCacheRecord *)NULL) { + record->add_dependent_file(vf); + } _last_modified = max(_last_modified, vf->get_timestamp()); _source_files.push_back(vf->get_filename()); } @@ -2368,7 +2410,8 @@ do_read_source(string &into, const Filename &fn) { bool Shader:: r_preprocess_source(ostream &out, const Filename &fn, const Filename &source_dir, - set &once_files, int depth) { + set &once_files, + BamCacheRecord *record, int depth) { if (depth > glsl_include_recursion_limit) { shader_cat.error() @@ -2402,6 +2445,9 @@ r_preprocess_source(ostream &out, const Filename &fn, return false; } + if (record != (BamCacheRecord *)NULL) { + record->add_dependent_file(vf); + } _last_modified = max(_last_modified, vf->get_timestamp()); _source_files.push_back(full_fn); @@ -2483,7 +2529,7 @@ r_preprocess_source(ostream &out, const Filename &fn, } // 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. shader_cat.error(false) << "included at line " << lineno << " of file " << fn << ":\n " << line << "\n"; @@ -2768,6 +2814,14 @@ load_compute(ShaderLanguage lang, const Filename &fn) { 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; sfile._separate = true; 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); - if (!shader->read(sfile)) { + + if (!shader->read(sfile, record)) { 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; return shader; } @@ -3195,11 +3267,9 @@ clear() { _active_vprofile = CG_PROFILE_UNKNOWN; _active_fprofile = CG_PROFILE_UNKNOWN; _active_gprofile = CG_PROFILE_UNKNOWN; - _active_fprofile = CG_PROFILE_UNKNOWN; _ultimate_vprofile = CG_PROFILE_UNKNOWN; _ultimate_fprofile = CG_PROFILE_UNKNOWN; _ultimate_gprofile = CG_PROFILE_UNKNOWN; - _ultimate_fprofile = CG_PROFILE_UNKNOWN; #endif } @@ -3211,7 +3281,7 @@ clear() { //////////////////////////////////////////////////////////////////// void Shader:: 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); _filename.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(); _filename.read_datagram(scan); _text.read_datagram(scan); + + _compiled_format = scan.get_uint32(); + _compiled_binary = scan.get_string(); } diff --git a/panda/src/gobj/shader.h b/panda/src/gobj/shader.h index ace9e76212..13b8f749a5 100644 --- a/panda/src/gobj/shader.h +++ b/panda/src/gobj/shader.h @@ -40,6 +40,8 @@ typedef struct _CGprogram *CGprogram; typedef struct _CGparameter *CGparameter; #endif +class BamCacheRecord; + //////////////////////////////////////////////////////////////////// // Class : Shader // Summary: The Shader class is meant to select the Shader Language, @@ -103,9 +105,11 @@ PUBLISHED: INLINE bool get_error_flag() 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); bool is_prepared(PreparedGraphicsObjects *prepared_objects) const; @@ -507,6 +511,9 @@ public: 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: #ifdef HAVE_CG ShaderArgClass cg_parameter_class(CGparameter p); @@ -557,18 +564,25 @@ public: protected: ShaderFile _filename; + Filename _fullpath; int _parse; bool _loaded; ShaderLanguage _language; - pvector _included_files; + + typedef pvector Filenames; + Filenames _included_files; // Stores full paths, and includes the fullpaths of the shaders // themselves as well as the includes. - pvector _source_files; + Filenames _source_files; time_t _last_modified; + PT(BamCacheRecord) _record; + bool _cache_compiled_shader; + unsigned int _compiled_format; + string _compiled_binary; + static ShaderCaps _default_caps; - static ShaderUtilization _shader_utilization; static int _shaders_generated; typedef pmap ShaderTable; @@ -587,11 +601,12 @@ private: 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, const Filename &source_dir, - set &open_files, int depth = 0); + set &open_files, + BamCacheRecord *record, int depth = 0); bool check_modified() const; diff --git a/panda/src/pgraph/config_pgraph.cxx b/panda/src/pgraph/config_pgraph.cxx index 2c879aba12..319660780d 100644 --- a/panda/src/pgraph/config_pgraph.cxx +++ b/panda/src/pgraph/config_pgraph.cxx @@ -504,7 +504,6 @@ init_libpgraph() { ShadeModelAttrib::register_with_read_factory(); ShaderInput::register_with_read_factory(); ShaderAttrib::register_with_read_factory(); - Shader::register_with_read_factory(); ShowBoundsEffect::register_with_read_factory(); TexMatrixAttrib::register_with_read_factory(); TexProjectorEffect::register_with_read_factory(); diff --git a/panda/src/putil/bamCache.I b/panda/src/putil/bamCache.I index 9527bd0a83..8345f02b1d 100644 --- a/panda/src/putil/bamCache.I +++ b/panda/src/putil/bamCache.I @@ -142,6 +142,35 @@ get_cache_compressed_textures() const { 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 // Access: Published diff --git a/panda/src/putil/bamCache.cxx b/panda/src/putil/bamCache.cxx index 23e8dece01..0b7871475a 100644 --- a/panda/src/putil/bamCache.cxx +++ b/panda/src/putil/bamCache.cxx @@ -74,6 +74,12 @@ BamCache() : "by the GSG. This may be set in conjunction with " "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 ("model-cache-max-kbytes", 10485760, PRC_DESC("This is the maximum size of the model cache, in kilobytes.")); @@ -81,6 +87,7 @@ BamCache() : _cache_models = model_cache_models; _cache_textures = model_cache_textures; _cache_compressed_textures = model_cache_compressed_textures; + _cache_compiled_shaders = model_cache_compiled_shaders; _flush_time = model_cache_flush; _max_kbytes = model_cache_max_kbytes; diff --git a/panda/src/putil/bamCache.h b/panda/src/putil/bamCache.h index f99ba321d9..583cf2a0be 100644 --- a/panda/src/putil/bamCache.h +++ b/panda/src/putil/bamCache.h @@ -61,6 +61,9 @@ PUBLISHED: INLINE void set_cache_compressed_textures(bool flag); 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); INLINE Filename get_root() const; @@ -92,6 +95,8 @@ PUBLISHED: MAKE_PROPERTY(cache_textures, get_cache_textures, set_cache_textures); MAKE_PROPERTY(cache_compressed_textures, get_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(flush_time, get_flush_time, set_flush_time); MAKE_PROPERTY(cache_max_kbytes, get_cache_max_kbytes, set_cache_max_kbytes); @@ -130,6 +135,7 @@ private: bool _cache_models; bool _cache_textures; bool _cache_compressed_textures; + bool _cache_compiled_shaders; bool _read_only; Filename _root; int _flush_time; diff --git a/panda/src/putil/bamCacheRecord.cxx b/panda/src/putil/bamCacheRecord.cxx index 79a12ea2da..daae57af45 100644 --- a/panda/src/putil/bamCacheRecord.cxx +++ b/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 // Access: Published diff --git a/panda/src/putil/bamCacheRecord.h b/panda/src/putil/bamCacheRecord.h index 523fadc78a..fd3d121e4b 100644 --- a/panda/src/putil/bamCacheRecord.h +++ b/panda/src/putil/bamCacheRecord.h @@ -26,6 +26,7 @@ class Datagram; class DatagramIterator; class FactoryParams; class BamCacheRecord; +class VirtualFile; //////////////////////////////////////////////////////////////////// // Class : BamCacheRecord @@ -66,6 +67,7 @@ PUBLISHED: bool dependents_unchanged() const; void clear_dependent_files(); void add_dependent_file(const Filename &pathname); + void add_dependent_file(const VirtualFile *file); INLINE bool has_data() const; INLINE void clear_data();