From 8ef24aa846651ac6c04880a362b46674ebbd5135 Mon Sep 17 00:00:00 2001 From: rdb Date: Wed, 13 Mar 2013 09:47:37 +0000 Subject: [PATCH] clean up shader code, add more debugging info, make shaders writable --- .../glstuff/glGraphicsStateGuardian_src.cxx | 13 +- panda/src/gobj/shader.I | 124 +++++++++++ panda/src/gobj/shader.cxx | 197 ++++++++++++++---- panda/src/gobj/shader.h | 115 +++++----- 4 files changed, 347 insertions(+), 102 deletions(-) diff --git a/panda/src/glstuff/glGraphicsStateGuardian_src.cxx b/panda/src/glstuff/glGraphicsStateGuardian_src.cxx index 1f3a3a977a..4940efdd79 100644 --- a/panda/src/glstuff/glGraphicsStateGuardian_src.cxx +++ b/panda/src/glstuff/glGraphicsStateGuardian_src.cxx @@ -1790,11 +1790,20 @@ reset() { CGprofile vertex_profile; CGprofile pixel_profile; - vertex_profile = cgGLGetLatestProfile (CG_GL_VERTEX); - pixel_profile = cgGLGetLatestProfile (CG_GL_FRAGMENT); + vertex_profile = cgGLGetLatestProfile(CG_GL_VERTEX); + pixel_profile = cgGLGetLatestProfile(CG_GL_FRAGMENT); if (GLCAT.is_debug()) { // Temp ifdef: this crashes Mesa. #ifndef OSMESA_MAJOR_VERSION + GLCAT.debug() << "Supported Cg profiles:\n"; + int num_profiles = cgGetNumSupportedProfiles(); + for (int i = 0; i < num_profiles; ++i) { + CGprofile profile = cgGetSupportedProfile(i); + if (cgGLIsProfileSupported(profile)) { + GLCAT.debug() << " " << cgGetProfileString(profile) << "\n"; + } + } + GLCAT.debug() << "\nCg vertex profile = " << cgGetProfileString(vertex_profile) << " id = " << vertex_profile << "\nCg pixel profile = " << cgGetProfileString(pixel_profile) << " id = " << pixel_profile diff --git a/panda/src/gobj/shader.I b/panda/src/gobj/shader.I index 9a539928b0..858aebc982 100755 --- a/panda/src/gobj/shader.I +++ b/panda/src/gobj/shader.I @@ -542,3 +542,127 @@ ShaderPtrData(const LMatrix3d &mat) : nassertv(sizeof(mat(0, 0)) * mat.get_num_components() == pta.size() * sizeof(pta[0])); memcpy(_ptr, mat.get_data(), sizeof(mat(0, 0)) * mat.get_num_components()); } + +//////////////////////////////////////////////////////////////////// +// Function: Shader::ShaderPtrData::write_datagram +// Access: Public +// Description: Writes the contents of this object to the datagram +// for shipping out to a Bam file. +//////////////////////////////////////////////////////////////////// +INLINE void Shader::ShaderPtrData:: +write_datagram(Datagram &dg) const { + dg.add_uint8(_type); + dg.add_uint32(_size); + + if (_type == SPT_double) { + const double *data = (const double *) _ptr; + for (size_t i = 0; i < _size; ++i) { + dg.add_float64(data[i]); + } + + } else if (_type == SPT_float) { + const float *data = (const float *) _ptr; + for (size_t i = 0; i < _size; ++i) { + dg.add_float32(data[i]); + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: Shader::ShaderPtrData::write_datagram +// Access: Public +// Description: Reads the object from a Datagram. +//////////////////////////////////////////////////////////////////// +INLINE void Shader::ShaderPtrData:: +read_datagram(DatagramIterator &scan) { + _type = (ShaderPtrType) scan.get_uint8(); + _size = scan.get_uint32(); + + if (_type == SPT_double) { + PTA_double pta = PTA_double::empty_array(_size); + for (size_t i = 0; i < _size; ++i) { + pta[i] = scan.get_float64(); + } + _pta = pta.v0(); + _ptr = pta.p(); + + } else if (_type == SPT_float) { + PTA_float pta = PTA_float::empty_array(_size); + for (size_t i = 0; i < _size; ++i) { + pta[i] = scan.get_float32(); + } + _pta = pta.v0(); + _ptr = pta.p(); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: Shader::ShaderFile::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE Shader::ShaderFile:: +ShaderFile(const string &shared) : + _separate(false), + _shared(shared) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: Shader::ShaderFile::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE Shader::ShaderFile:: +ShaderFile(const string &vertex, + const string &fragment, + const string &geometry, + const string &tess_control, + const string &tess_evaluation) : + _separate(false), + _vertex(vertex), + _fragment(fragment), + _geometry(geometry), + _tess_control(tess_control), + _tess_evaluation(tess_evaluation) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: Shader::ShaderFile::write_datagram +// Access: Public +// Description: Writes the contents of this object to the datagram +// for shipping out to a Bam file. +//////////////////////////////////////////////////////////////////// +INLINE void Shader::ShaderFile:: +write_datagram(Datagram &dg) const { + dg.add_bool(_separate); + if (_separate) { + dg.add_string(_vertex); + dg.add_string(_fragment); + dg.add_string(_geometry); + dg.add_string(_tess_control); + dg.add_string(_tess_evaluation); + } else { + dg.add_string(_shared); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: Shader::ShaderFile::write_datagram +// Access: Public +// Description: Reads the object from a Datagram. +//////////////////////////////////////////////////////////////////// +INLINE void Shader::ShaderFile:: +read_datagram(DatagramIterator &scan) { + _separate = scan.get_bool(); + if (_separate) { + _vertex = scan.get_string(); + _fragment = scan.get_string(); + _geometry = scan.get_string(); + _tess_control = scan.get_string(); + _tess_evaluation = scan.get_string(); + } else { + _shared = scan.get_string(); + } +} diff --git a/panda/src/gobj/shader.cxx b/panda/src/gobj/shader.cxx index 0becf5cf9e..909332c3a4 100755 --- a/panda/src/gobj/shader.cxx +++ b/panda/src/gobj/shader.cxx @@ -1405,6 +1405,22 @@ cg_compile_entry_point(const char *entry, const ShaderCaps &caps, ShaderType typ compiler_args[nargs] = 0; if ((active != (int)CG_PROFILE_UNKNOWN) && (active != ultimate)) { + // Print out some debug information about what we're doing. + if (gobj_cat.is_debug()) { + gobj_cat.debug() + << "Compiling Cg shader " << get_filename(type) << " with entry point " << entry + << " and active profile " << cgGetProfileString((CGprofile) active) << "\n"; + + if (nargs > 0) { + gobj_cat.debug() << "Using compiler arguments:"; + for (int i = 0; i < nargs; ++i) { + gobj_cat.debug(false) << " " << compiler_args[i]; + } + gobj_cat.debug(false) << "\n"; + } + } + + // Compile the shader with the active profile. prog = cgCreateProgram(_cg_context, CG_SOURCE, text.c_str(), (CGprofile)active, entry, (const char **)compiler_args); err = cgGetError(); @@ -1414,8 +1430,19 @@ cg_compile_entry_point(const char *entry, const ShaderCaps &caps, ShaderType typ if (prog != 0) { cgDestroyProgram(prog); } + if (gobj_cat.is_debug()) { + gobj_cat.debug() + << "Compilation with active profile failed: " << cgGetErrorString(err) << "\n"; + } } + if (gobj_cat.is_debug()) { + gobj_cat.debug() + << "Compiling Cg shader " << get_filename(type) << " with entry point " << entry + << " and ultimate profile " << cgGetProfileString((CGprofile) ultimate) << "\n"; + } + + // The active profile failed, so recompile it with the ultimate profile. prog = cgCreateProgram(_cg_context, CG_SOURCE, text.c_str(), (CGprofile)ultimate, entry, (const char **)NULL); err = cgGetError(); @@ -1423,10 +1450,11 @@ cg_compile_entry_point(const char *entry, const ShaderCaps &caps, ShaderType typ return prog; } if (err == CG_COMPILER_ERROR) { + // A compiler error has occurred. Extract the error messages. string listing = cgGetLastListing(_cg_context); vector_string errlines; tokenize(listing, errlines, "\n"); - for (int i=0; i<(int)errlines.size(); i++) { + for (int i = 0; i < (int) errlines.size(); ++i) { string line = trim(errlines[i]); if (line != "") { gobj_cat.error() << get_filename(type) << ": " << errlines[i] << "\n"; @@ -1466,12 +1494,8 @@ cg_compile_shader(const ShaderCaps &caps) { _cg_context = cgCreateContext(); - 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"; + gobj_cat.error() << "Could not create a Cg context object.\n"; return false; } @@ -1481,6 +1505,7 @@ cg_compile_shader(const ShaderCaps &caps) { cg_release_resources(); return false; } + _cg_vprofile = cgGetProgramProfile(_cg_vprogram); } if (!_text->_separate || !_text->_fragment.empty()) { @@ -1489,6 +1514,7 @@ cg_compile_shader(const ShaderCaps &caps) { cg_release_resources(); return false; } + _cg_fprofile = cgGetProgramProfile(_cg_fprogram); } if ((_text->_separate && !_text->_geometry.empty()) || (!_text->_separate && _text->_shared.find("gshader") != string::npos)) { @@ -1497,6 +1523,7 @@ cg_compile_shader(const ShaderCaps &caps) { cg_release_resources(); return false; } + _cg_gprofile = cgGetProgramProfile(_cg_gprogram); } if (_cg_vprogram == 0 && _cg_fprogram == 0 && _cg_gprogram == 0) { @@ -1508,19 +1535,25 @@ cg_compile_shader(const ShaderCaps &caps) { // DEBUG: output the generated program if (gobj_cat.is_debug()) { const char *vertex_program; - const char *pixel_program; + const char *fragment_program; const char *geometry_program; if (_cg_vprogram != 0) { - vertex_program = cgGetProgramString (_cg_vprogram, CG_COMPILED_PROGRAM); + gobj_cat.debug() + << "Cg vertex profile: " << cgGetProfileString((CGprofile)_cg_vprofile) << "\n"; + vertex_program = cgGetProgramString(_cg_vprogram, CG_COMPILED_PROGRAM); gobj_cat.debug() << vertex_program << "\n"; } if (_cg_fprogram != 0) { - pixel_program = cgGetProgramString (_cg_fprogram, CG_COMPILED_PROGRAM); - gobj_cat.debug() << pixel_program << "\n"; + gobj_cat.debug() + << "Cg fragment profile: " << cgGetProfileString((CGprofile)_cg_fprofile) << "\n"; + fragment_program = cgGetProgramString(_cg_fprogram, CG_COMPILED_PROGRAM); + gobj_cat.debug() << fragment_program << "\n"; } if (_cg_gprogram != 0) { - geometry_program = cgGetProgramString (_cg_gprogram, CG_COMPILED_PROGRAM); + gobj_cat.debug() + << "Cg geometry profile: " << cgGetProfileString((CGprofile)_cg_gprofile) << "\n"; + geometry_program = cgGetProgramString(_cg_gprogram, CG_COMPILED_PROGRAM); gobj_cat.debug() << geometry_program << "\n"; } } @@ -1752,7 +1785,6 @@ cg_compile_for(const ShaderCaps &caps, pvector &map) { // Initialize the return values to empty. - ctx = 0; vprogram = 0; fprogram = 0; @@ -1771,31 +1803,23 @@ cg_compile_for(const ShaderCaps &caps, // If the compile routine used the ultimate profile instead of the // active one, it means the active one isn't powerful enough to // compile the shader. - - if (_filename->_separate) { - if (_cg_vprogram != 0 && _cg_vprofile == CG_PROFILE_UNKNOWN && cgGetProgramProfile(_cg_vprogram) != caps._active_vprofile) { - gobj_cat.error() << "Cg vprogram too complex for driver: " - << get_filename(ST_vertex) << ". Try choosing a different profile.\n"; - return false; - } - if (_cg_fprogram != 0 && _cg_fprofile == CG_PROFILE_UNKNOWN && cgGetProgramProfile(_cg_fprogram) != caps._active_fprofile) { - gobj_cat.error() << "Cg fprogram too complex for driver: " - << get_filename(ST_fragment) << ". Try choosing a different profile.\n"; - return false; - } - if (_cg_gprogram != 0 && _cg_gprofile == CG_PROFILE_UNKNOWN && cgGetProgramProfile(_cg_gprogram) != caps._active_gprofile) { - gobj_cat.error() << "Cg gprogram too complex for driver: " - << get_filename(ST_geometry) << ". Try choosing a different profile.\n"; - return false; - } - } else { - if ((_cg_vprogram != 0 && _cg_vprofile == CG_PROFILE_UNKNOWN && cgGetProgramProfile(_cg_vprogram) != caps._active_vprofile) || - (_cg_fprogram != 0 && _cg_fprofile == CG_PROFILE_UNKNOWN && cgGetProgramProfile(_cg_fprogram) != caps._active_fprofile) || - (_cg_gprogram != 0 && _cg_gprofile == CG_PROFILE_UNKNOWN && cgGetProgramProfile(_cg_gprogram) != caps._active_gprofile)) { - gobj_cat.error() << "Cg program too complex for driver: " - << get_filename() << ". Try choosing a different profile.\n"; - return false; - } + if (_cg_vprogram != 0 && _cg_vprofile != caps._active_vprofile) { + gobj_cat.error() << "Cg vertex program not supported by profile " + << cgGetProfileString((CGprofile) caps._active_vprofile) << ": " + << get_filename(ST_vertex) << ". Try choosing a different profile.\n"; + return false; + } + if (_cg_fprogram != 0 && _cg_fprofile != caps._active_fprofile) { + gobj_cat.error() << "Cg vertex program not supported by profile " + << cgGetProfileString((CGprofile) caps._active_fprofile) << ": " + << get_filename(ST_fragment) << ". Try choosing a different profile.\n"; + return false; + } + if (_cg_gprogram != 0 && _cg_gprofile != caps._active_gprofile) { + gobj_cat.error() << "Cg vertex program not supported by profile " + << cgGetProfileString((CGprofile) caps._active_gprofile) << ": " + << get_filename(ST_fragment) << ". Try choosing a different profile.\n"; + return false; } // Build a parameter map. @@ -1854,6 +1878,43 @@ cg_compile_for(const ShaderCaps &caps, } #endif // HAVE_CG +//////////////////////////////////////////////////////////////////// +// Function: Shader::Constructor +// Access: Private +// Description: Construct a Shader that will be filled in using +// fillin() later. +//////////////////////////////////////////////////////////////////// +Shader:: +Shader() : + _error_flag(false), + _text(NULL), + _filename(NULL), + _parse(0), + _loaded(false), + _language(SL_none) +{ +#ifdef HAVE_CG + _cg_context = 0; + _cg_vprogram = 0; + _cg_fprogram = 0; + _cg_gprogram = 0; + _cg_vprofile = CG_PROFILE_UNKNOWN; + _cg_fprofile = CG_PROFILE_UNKNOWN; + _cg_gprofile = CG_PROFILE_UNKNOWN; + if (_default_caps._ultimate_vprofile == 0 || _default_caps._ultimate_vprofile == CG_PROFILE_UNKNOWN) { + _default_caps._active_vprofile = CG_PROFILE_UNKNOWN; + _default_caps._active_fprofile = CG_PROFILE_UNKNOWN; + _default_caps._active_gprofile = CG_PROFILE_UNKNOWN; + _default_caps._ultimate_vprofile = cgGetProfile("glslv"); + _default_caps._ultimate_fprofile = cgGetProfile("glslf"); + _default_caps._ultimate_gprofile = cgGetProfile("glslg"); + if (_default_caps._ultimate_gprofile == CG_PROFILE_UNKNOWN) { + _default_caps._ultimate_gprofile = cgGetProfile("gp4gp"); + } + } +#endif +} + //////////////////////////////////////////////////////////////////// // Function: Shader::Constructor // Access: Private @@ -1861,15 +1922,13 @@ cg_compile_for(const ShaderCaps &caps, //////////////////////////////////////////////////////////////////// Shader:: Shader(CPT(ShaderFile) filename, CPT(ShaderFile) text, const ShaderLanguage &lang) : - _error_flag(true), + _error_flag(false), _text(text), _filename(filename), _parse(0), _loaded(false), _language(lang) { - _error_flag = false; - #ifdef HAVE_CG _cg_context = 0; _cg_vprogram = 0; @@ -2466,10 +2525,64 @@ clear() { //////////////////////////////////////////////////////////////////// // Function: Shader::register_with_read_factory // Access: Public, Static -// Description: +// Description: Tells the BamReader how to create objects of type +// Shader. //////////////////////////////////////////////////////////////////// void Shader:: register_with_read_factory() { - // IMPLEMENT ME + BamReader::get_factory()->register_factory(get_class_type(), make_from_bam); } +//////////////////////////////////////////////////////////////////// +// Function: Shader::write_datagram +// Access: Public, Virtual +// Description: Writes the contents of this object to the datagram +// for shipping out to a Bam file. +//////////////////////////////////////////////////////////////////// +void Shader:: +write_datagram(BamWriter *manager, Datagram &dg) { + dg.add_uint8(_language); + dg.add_bool(_loaded); + _filename->write_datagram(dg); + _text->write_datagram(dg); +} + +//////////////////////////////////////////////////////////////////// +// Function: Shader::make_from_bam +// Access: Protected, Static +// Description: This function is called by the BamReader's factory +// when a new object of type Shader is encountered +// in the Bam file. It should create the Shader +// and extract its information from the file. +//////////////////////////////////////////////////////////////////// +TypedWritable *Shader:: +make_from_bam(const FactoryParams ¶ms) { + Shader *attrib = new Shader; + DatagramIterator scan; + BamReader *manager; + + parse_params(params, scan, manager); + attrib->fillin(scan, manager); + return attrib; +} + +//////////////////////////////////////////////////////////////////// +// Function: Shader::fillin +// Access: Protected +// Description: This internal function is called by make_from_bam to +// read in all of the relevant data from the BamFile for +// the new Shader. +//////////////////////////////////////////////////////////////////// +void Shader:: +fillin(DatagramIterator &scan, BamReader *manager) { + _language = (ShaderLanguage) scan.get_uint8(); + _loaded = scan.get_bool(); + + PT(ShaderFile) filename = new ShaderFile; + filename->read_datagram(scan); + _filename = filename; + + PT(ShaderFile) text = new ShaderFile; + text->read_datagram(scan); + _text = text; +} diff --git a/panda/src/gobj/shader.h b/panda/src/gobj/shader.h index 71c7312829..7969b6e995 100755 --- a/panda/src/gobj/shader.h +++ b/panda/src/gobj/shader.h @@ -16,7 +16,7 @@ #define SHADER_H #include "pandabase.h" -#include "typedReferenceCount.h" +#include "typedWritableReferenceCount.h" #include "namable.h" #include "graphicsStateGuardianBase.h" #include "internalName.h" @@ -44,10 +44,8 @@ typedef struct _CGparameter *CGparameter; // finally compile and store the shader parameters // in the appropriate structure. //////////////////////////////////////////////////////////////////// -class EXPCL_PANDA_GOBJ Shader: public TypedReferenceCount { - +class EXPCL_PANDA_GOBJ Shader : public TypedWritableReferenceCount { PUBLISHED: - enum ShaderLanguage { SL_none, SL_Cg, @@ -110,7 +108,6 @@ PUBLISHED: GraphicsStateGuardianBase *gsg); public: - enum ShaderMatInput { SMO_identity, @@ -311,6 +308,9 @@ public: INLINE ShaderPtrData(const LVecBase2d &vec); INLINE ShaderPtrData(const LMatrix4d &mat); INLINE ShaderPtrData(const LMatrix3d &mat); + + INLINE void write_datagram(Datagram &dg) const; + INLINE void read_datagram(DatagramIterator &source); }; struct ShaderMatSpec { @@ -372,22 +372,16 @@ public: class ShaderFile : public ReferenceCount { public: - ShaderFile(const string &shared) { - _separate = false; - _shared = shared; - } - ShaderFile(const string &vertex, - const string &fragment, - const string &geometry, - const string &tess_control, - const string &tess_evaluation) { - _separate = true; - _vertex = vertex; - _fragment = fragment; - _geometry = geometry; - _tess_control = tess_control; - _tess_evaluation = tess_evaluation; - } + INLINE ShaderFile() {}; + INLINE ShaderFile(const string &shared); + INLINE ShaderFile(const string &vertex, + const string &fragment, + const string &geometry, + const string &tess_control, + const string &tess_evaluation); + + INLINE void write_datagram(Datagram &dg) const; + INLINE void read_datagram(DatagramIterator &source); public: bool _separate; @@ -399,7 +393,7 @@ public: string _tess_evaluation; }; - public: +public: // 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. @@ -446,23 +440,23 @@ public: void clear_parameters(); #ifdef HAVE_CG - private: - ShaderArgClass cg_parameter_class(CGparameter p); - ShaderArgType cg_parameter_type(CGparameter p); - ShaderArgDir cg_parameter_dir(CGparameter p); +private: + ShaderArgClass cg_parameter_class(CGparameter p); + ShaderArgType cg_parameter_type(CGparameter p); + ShaderArgDir cg_parameter_dir(CGparameter p); - CGprogram cg_compile_entry_point(const char *entry, const ShaderCaps &caps, ShaderType type = ST_vertex); + CGprogram cg_compile_entry_point(const char *entry, const ShaderCaps &caps, ShaderType type = ST_vertex); - bool cg_analyze_entry_point(CGprogram prog, ShaderType type); + bool cg_analyze_entry_point(CGprogram prog, ShaderType type); - bool cg_analyze_shader(const ShaderCaps &caps); - bool cg_compile_shader(const ShaderCaps &caps); - void cg_release_resources(); - void cg_report_errors(); + bool cg_analyze_shader(const ShaderCaps &caps); + bool cg_compile_shader(const ShaderCaps &caps); + void cg_release_resources(); + void cg_report_errors(); // Determines the appropriate cg profile settings and stores them in the active shader caps // based on any profile settings stored in the shader's header - void cg_get_profile_from_header(ShaderCaps &caps); + void cg_get_profile_from_header(ShaderCaps &caps); ShaderCaps _cg_last_caps; CGcontext _cg_context; @@ -470,36 +464,33 @@ public: CGprogram _cg_fprogram; CGprogram _cg_gprogram; - int _cg_vprofile; - int _cg_fprofile; - int _cg_gprofile; + int _cg_vprofile; + int _cg_fprofile; + int _cg_gprofile; - CGprogram cg_program_from_shadertype(ShaderType type); + CGprogram cg_program_from_shadertype(ShaderType type); - public: +public: - bool cg_compile_for(const ShaderCaps &caps, - CGcontext &ctx, - CGprogram &vprogram, - CGprogram &fprogram, - CGprogram &gprogram, - pvector &map); + bool cg_compile_for(const ShaderCaps &caps, CGcontext &ctx, + CGprogram &vprogram, CGprogram &fprogram, + CGprogram &gprogram, pvector &map); #endif - public: +public: pvector _ptr_spec; epvector _mat_spec; pvector _tex_spec; pvector _var_spec; - bool _error_flag; + bool _error_flag; CPT(ShaderFile) _text; - protected: +protected: CPT(ShaderFile) _filename; - int _parse; - bool _loaded; + int _parse; + bool _loaded; ShaderLanguage _language; static ShaderCaps _default_caps; @@ -517,30 +508,38 @@ public: typedef pmap Contexts; Contexts _contexts; - private: +private: void clear_prepared(PreparedGraphicsObjects *prepared_objects); - public: + Shader(); + +public: Shader(CPT(ShaderFile) name, CPT(ShaderFile) text, const ShaderLanguage &lang = SL_none); - static void register_with_read_factory(); - ~Shader(); - - public: + +public: + static void register_with_read_factory(); + virtual void write_datagram(BamWriter *manager, Datagram &dg); + +protected: + static TypedWritable *make_from_bam(const FactoryParams ¶ms); + void fillin(DatagramIterator &scan, BamReader *manager); + +public: static TypeHandle get_class_type() { return _type_handle; } static void init_type() { - TypedReferenceCount::init_type(); + TypedWritableReferenceCount::init_type(); register_type(_type_handle, "Shader", - TypedReferenceCount::get_class_type()); + TypedWritableReferenceCount::get_class_type()); } virtual TypeHandle get_type() const { return get_class_type(); } virtual TypeHandle force_init_type() {init_type(); return get_class_type();} - private: +private: static TypeHandle _type_handle; };