diff --git a/panda/src/display/graphicsEngine.cxx b/panda/src/display/graphicsEngine.cxx index beec7e15be..78993aa481 100644 --- a/panda/src/display/graphicsEngine.cxx +++ b/panda/src/display/graphicsEngine.cxx @@ -19,6 +19,7 @@ #include "graphicsEngine.h" #include "graphicsPipe.h" #include "parasiteBuffer.h" +#include "config_gobj.h" #include "config_display.h" #include "pipeline.h" #include "drawCullHandler.h" @@ -1761,7 +1762,8 @@ do_add_window(GraphicsOutput *window, // Access: Private // Description: An internal function called by make_output to add // the newly-created gsg object to the engine's -// list of gsg's. +// list of gsg's. It also adjusts various config +// variables based on the gsg's capabilities. //////////////////////////////////////////////////////////////////// void GraphicsEngine:: do_add_gsg(GraphicsStateGuardian *gsg, GraphicsPipe *pipe, @@ -1772,6 +1774,8 @@ do_add_gsg(GraphicsStateGuardian *gsg, GraphicsPipe *pipe, gsg->_pipe = pipe; gsg->_engine = this; + auto_adjust_capabilities(gsg); + WindowRenderer *draw = get_window_renderer(threading_model.get_draw_name(), threading_model.get_draw_stage()); @@ -1835,6 +1839,155 @@ do_resort_windows() { _windows.sort(); } +//////////////////////////////////////////////////////////////////// +// Function: GraphicsEngine::auto_adjust_capabilities +// Access: Private +// Description: Video card capability flags are stored on a +// per-gsg basis. However, there are a few cases +// where panda needs to know not the capabilities +// of an individual GSG, but rather, the +// collective capabilities of all the GSGs. +// +// Non-power-of-two (NPOT) texture support is the +// classic example. Panda makes a single global +// decision to either create NPOT textures, or not. +// Therefore, it doesn't need to know whether one GSG +// supports NPOT textures. It needs to know whether ALL +// the GSGs support NPOT textures. +// +// The purpose of this routine is to maintain global +// capability flags that summarize the collective +// capabilities of the computer as a whole. +// +// These global capability flags are initialized from +// config variables. Then, they can be auto-reconfigured +// using built-in heuristic mechanisms if the user so +// desires. Whether auto-reconfiguration is enabled or +// not, the configured values are checked against +// the actual capabilities of the machine and error +// messages will be printed if there is a mismatch. +// +//////////////////////////////////////////////////////////////////// +void GraphicsEngine:: +auto_adjust_capabilities(GraphicsStateGuardian *gsg) { + + // The rule we use when auto-reconfiguring is as follows. The + // global capabilities must initially be set to conservative + // values. When the first GSG comes into existence, its + // capabilities will be checked, and the global capabilities + // may be elevated to more aggressive values. + // + // At first glance, this might seem backward, and it might seem + // better to do it the other way: start with all global capabilities + // aggressively set, and then disable capabilities when you discover + // a gsg that doesn't support them. + // + // However, that approach doesn't work, because once a global + // capability is enabled, there is no going back. If + // textures_power_2 has ever been set to 'none', there may be NPOT + // textures already floating about the system. Ie, it's too late: + // you can't turn these global capability flags off, once they've + // been turned on. + // + // That's why we have to start with conservative settings, and then + // elevate those settings to more aggressive values later when + // we're fairly sure it's OK to do so. + // + // For each global capability, we must: + // 1. Make sure the initial setting is conservative. + // 2. Possibly elevate to a more aggressive value. + // 3. Check that we haven't over-elevated. + // + + if (textures_auto_power_2 && (textures_power_2 == ATS_none)) { + display_cat.error() + << "Invalid panda config file: if you set the config-variable\n" + << "textures_auto_power_2 to true, you must set the config-variable" + << "textures_power_2 to 'up' or 'down'.\n"; + textures_power_2 = ATS_down; // Not a fix. Just suppresses further error messages. + } + + if (textures_auto_power_2 && !Texture::have_textures_power_2()) { + if (gsg->get_supports_tex_non_pow2()) { + Texture::set_textures_power_2(ATS_none); + } else { + Texture::set_textures_power_2(textures_power_2); + } + } + + if ((Texture::get_textures_power_2() == ATS_none) && + (!gsg->get_supports_tex_non_pow2())) { + + // Overaggressive configuration detected + + display_cat.error() + << "The 'textures_power_2' configuration is set to 'none', meaning \n" + << "that non-power-of-two texture support is required, but the video \n" + << "driver I'm trying to use does not support non-power-of-two textures.\n"; + + if (textures_power_2 != ATS_none) { + display_cat.error() + << "The 'none' did not come from the config file. In other words,\n" + << "the variable 'textures_power_2' was altered procedurally.\n"; + + if (textures_auto_power_2) { + display_cat.error() + << "It is possible that it was set by panda's automatic mechanisms,\n" + << "which are currently enabled, because 'textures_auto_power_2' is\n" + << "true. Panda's automatic mechanisms assume that if one\n" + << "window supports non-power-of-two textures, 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"; + } + } + } + + 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"; + } + } + } +} + //////////////////////////////////////////////////////////////////// // Function: GraphicsEngine::terminate_threads // Access: Private diff --git a/panda/src/display/graphicsEngine.h b/panda/src/display/graphicsEngine.h index 4298df9163..d1342ff102 100644 --- a/panda/src/display/graphicsEngine.h +++ b/panda/src/display/graphicsEngine.h @@ -124,6 +124,7 @@ public: bool remove_callback(const string &thread_name, CallbackTime callback_time, CallbackFunction *func, void *data); + private: class Callback { public: @@ -172,6 +173,7 @@ private: void do_remove_window(GraphicsOutput *window, Thread *current_thread); void do_resort_windows(); void terminate_threads(Thread *current_thread); + void auto_adjust_capabilities(GraphicsStateGuardian *gsg); #ifdef DO_PSTATS typedef map CyclerTypeCounters; diff --git a/panda/src/display/graphicsOutput.cxx b/panda/src/display/graphicsOutput.cxx index 4e6e62fa4b..7d3f7036b8 100644 --- a/panda/src/display/graphicsOutput.cxx +++ b/panda/src/display/graphicsOutput.cxx @@ -268,6 +268,7 @@ clear_render_textures() { void GraphicsOutput:: add_render_texture(Texture *tex, RenderTextureMode mode, RenderTexturePlane plane) { + if (mode == RTM_none) { return; } @@ -315,7 +316,7 @@ add_render_texture(Texture *tex, RenderTextureMode mode, // Go ahead and tell the texture our anticipated size, even if it // might be inaccurate (particularly if this is a GraphicsWindow, // which has system-imposed restrictions on size). - if (textures_power_2 != ATS_none) { + if (Texture::get_textures_power_2() != ATS_none) { tex->set_x_size(Texture::up_to_power_2(get_x_size())); tex->set_y_size(Texture::up_to_power_2(get_y_size())); } else { @@ -590,7 +591,7 @@ create_texture_card_vdata(int x, int y) float xhi = 1.0; float yhi = 1.0; - if (textures_power_2 != ATS_none) { + if (Texture::get_textures_power_2() != ATS_none) { int xru = Texture::up_to_power_2(x); int yru = Texture::up_to_power_2(y); xhi = (x * 1.0f) / xru; diff --git a/panda/src/display/graphicsStateGuardian.h b/panda/src/display/graphicsStateGuardian.h index d17a1fadad..f6e786cdd4 100644 --- a/panda/src/display/graphicsStateGuardian.h +++ b/panda/src/display/graphicsStateGuardian.h @@ -405,11 +405,11 @@ protected: bool _supports_stencil_wrap; bool _supports_two_sided_stencil; - int _supported_geom_rendering; + int _supported_geom_rendering; bool _color_scale_via_lighting; bool _alpha_scale_via_texture; - int _stereo_buffer_mask; + int _stereo_buffer_mask; StencilRenderStates *_stencil_render_states; diff --git a/panda/src/glstuff/glGraphicsBuffer_src.cxx b/panda/src/glstuff/glGraphicsBuffer_src.cxx index 9cf2c09d36..9815b1a27b 100644 --- a/panda/src/glstuff/glGraphicsBuffer_src.cxx +++ b/panda/src/glstuff/glGraphicsBuffer_src.cxx @@ -178,7 +178,7 @@ rebuild_bitplanes() { } int bitplane_x = _x_size; int bitplane_y = _y_size; - if (textures_power_2 != ATS_none) { + if (Texture::get_textures_power_2() != ATS_none) { bitplane_x = Texture::up_to_power_2(bitplane_x); bitplane_y = Texture::up_to_power_2(bitplane_y); } diff --git a/panda/src/gobj/config_gobj.cxx b/panda/src/gobj/config_gobj.cxx index 8d784d4af3..07256c04cc 100644 --- a/panda/src/gobj/config_gobj.cxx +++ b/panda/src/gobj/config_gobj.cxx @@ -228,6 +228,32 @@ ConfigVariableEnum textures_square "a square aspect ratio when they are loaded from disk. Set this " "to 'none', 'down', or 'up'. See textures-power-2.")); +ConfigVariableBool textures_auto_power_2 +("textures-auto-power-2", false, + PRC_DESC("If this is true, then panda will wait until you open a window, " + "and then ask the window if it supports non-power-of-two textures. " + "If so, then the config variable textures_power_2 will " + "automatically be adjusted. 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.")); + +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.")); + extern EXPCL_PANDA_GOBJ ConfigVariableBool textures_header_only; ConfigVariableBool textures_header_only ("textures-header-only", false, @@ -445,3 +471,47 @@ operator >> (istream &in, AutoTextureScale &ats) { return in; } + +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"; + } + + 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 || + tolower(word[0] == 'f')) { + sgc = SUT_none; + + } else if (cmp_nocase(word, "basic") == 0 || + cmp_nocase(word, "1") == 0 || + cmp_nocase(word, "#t") == 0 || + 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 84c87fd89c..1558b256bd 100644 --- a/panda/src/gobj/config_gobj.h +++ b/panda/src/gobj/config_gobj.h @@ -33,10 +33,20 @@ NotifyCategoryDecl(gobj, EXPCL_PANDA_GOBJ, EXPTP_PANDA_GOBJ); enum AutoTextureScale { ATS_none, ATS_down, - ATS_up + ATS_up, + ATS_UNSPECIFIED, }; +enum ShaderUtilization { + SUT_none, + SUT_basic, + SUT_advanced, + SUT_UNSPECIFIED, +}; + EXPCL_PANDA_GOBJ ostream &operator << (ostream &out, AutoTextureScale ats); EXPCL_PANDA_GOBJ istream &operator >> (istream &in, AutoTextureScale &ats); +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; @@ -59,8 +69,12 @@ extern EXPCL_PANDA_GOBJ ConfigVariableBool preserve_triangle_strips; extern EXPCL_PANDA_GOBJ ConfigVariableEnum textures_power_2; extern EXPCL_PANDA_GOBJ ConfigVariableEnum textures_square; +extern EXPCL_PANDA_GOBJ ConfigVariableBool textures_auto_power_2; extern EXPCL_PANDA_GOBJ ConfigVariableBool textures_header_only; +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 b41106af10..e40975c9e5 100755 --- a/panda/src/gobj/shader.I +++ b/panda/src/gobj/shader.I @@ -58,6 +58,49 @@ 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::ShaderCapabilities Constructor // Access: Public diff --git a/panda/src/gobj/shader.cxx b/panda/src/gobj/shader.cxx index 7ea5bfd38d..9393774873 100755 --- a/panda/src/gobj/shader.cxx +++ b/panda/src/gobj/shader.cxx @@ -29,6 +29,7 @@ TypeHandle Shader::_type_handle; Shader::LoadTable Shader::_load_table; Shader::MakeTable Shader::_make_table; Shader::ShaderCaps Shader::_default_caps; +ShaderUtilization Shader::_shader_utilization = SUT_UNSPECIFIED; //////////////////////////////////////////////////////////////////// // Function: Shader::cp_report_error diff --git a/panda/src/gobj/shader.h b/panda/src/gobj/shader.h index 9bd9f52d3e..107090bfcb 100755 --- a/panda/src/gobj/shader.h +++ b/panda/src/gobj/shader.h @@ -51,6 +51,10 @@ PUBLISHED: INLINE const string &get_header() const; INLINE bool get_error_flag() const; + INLINE static ShaderUtilization get_shader_utilization(); + INLINE static void set_shader_utilization(ShaderUtilization utl); + INLINE static bool have_shader_utilization(); + void prepare(PreparedGraphicsObjects *prepared_objects); bool is_prepared(PreparedGraphicsObjects *prepared_objects) const; bool release(PreparedGraphicsObjects *prepared_objects); @@ -268,6 +272,7 @@ public: bool _loaded; static ShaderCaps _default_caps; + static ShaderUtilization _shader_utilization; typedef pmap < Filename , Shader * > LoadTable; typedef pmap < string , Shader * > MakeTable; diff --git a/panda/src/gobj/texture.I b/panda/src/gobj/texture.I index bd089629d5..d89a3b4f9c 100644 --- a/panda/src/gobj/texture.I +++ b/panda/src/gobj/texture.I @@ -1381,3 +1381,45 @@ is_txo_filename(const Filename &fullpath) { #endif // HAVE_ZLIB return (extension == "txo"); } + +//////////////////////////////////////////////////////////////////// +// Function: Texture::set_textures_power_2 +// Access: Published, Static +// Description: Set this flag to ATS_none, ATS_up, or ATS_down +// to control the scaling of textures. +//////////////////////////////////////////////////////////////////// +INLINE void Texture:: +set_textures_power_2(AutoTextureScale scale) { + _textures_power_2 = scale; +} + +//////////////////////////////////////////////////////////////////// +// Function: Texture::get_textures_power_2 +// Access: Published, Static +// Description: This flag returns ATS_none, ATS_up, or ATS_down +// and controls the scaling of textures. It is +// initialized from the config variable of the same +// name, but it can be subsequently adjusted. +//////////////////////////////////////////////////////////////////// +INLINE AutoTextureScale Texture:: +get_textures_power_2() { + if (_textures_power_2 == ATS_UNSPECIFIED) { + return textures_power_2; + } else { + return _textures_power_2; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: Texture::have_textures_power_2 +// Access: Published, Static +// Description: If true, then get_textures_power_2 has been +// set using set_textures_power_2. +// If false, then get_textures_power_2 simply +// returns the config variable of the same name. +//////////////////////////////////////////////////////////////////// +INLINE bool Texture:: +have_textures_power_2() { + return (_textures_power_2 != ATS_UNSPECIFIED); +} + diff --git a/panda/src/gobj/texture.cxx b/panda/src/gobj/texture.cxx index dd9e0af502..2630b92647 100644 --- a/panda/src/gobj/texture.cxx +++ b/panda/src/gobj/texture.cxx @@ -44,6 +44,7 @@ PStatCollector Texture::_texture_read_pcollector("*:Texture:Read"); TypeHandle Texture::_type_handle; +AutoTextureScale Texture::_textures_power_2 = ATS_UNSPECIFIED; //////////////////////////////////////////////////////////////////// // Function: Texture::Constructor @@ -2909,7 +2910,7 @@ consider_rescale(PNMImage &pnmimage, const string &name) { new_y_size = (int)cfloor(new_y_size * texture_scale + 0.5); } - switch (textures_power_2.get_value()) { + switch (get_textures_power_2()) { case ATS_down: new_x_size = down_to_power_2(new_x_size); new_y_size = down_to_power_2(new_y_size); diff --git a/panda/src/gobj/texture.h b/panda/src/gobj/texture.h index 27535615d3..5193c6646b 100644 --- a/panda/src/gobj/texture.h +++ b/panda/src/gobj/texture.h @@ -323,6 +323,10 @@ PUBLISHED: void clear_aux_data(const string &key); TypedReferenceCount *get_aux_data(const string &key) const; + INLINE static void set_textures_power_2(AutoTextureScale scale); + INLINE static AutoTextureScale get_textures_power_2(); + INLINE static bool have_textures_power_2(); + PUBLISHED: // These are published, but in general, you shouldn't be mucking // with these values; they are set automatically when a texture is @@ -547,6 +551,7 @@ private: typedef pmap AuxData; AuxData _aux_data; + static AutoTextureScale _textures_power_2; static PStatCollector _texture_read_pcollector; // Datagram stuff diff --git a/panda/src/grutil/ffmpegTexture.cxx b/panda/src/grutil/ffmpegTexture.cxx index 99196b3f60..a8cf94feec 100644 --- a/panda/src/grutil/ffmpegTexture.cxx +++ b/panda/src/grutil/ffmpegTexture.cxx @@ -138,7 +138,7 @@ reconsider_video_properties(const FFMpegTexture::VideoStream &stream, int x_size = width; int y_size = height; - if (textures_power_2 != ATS_none) { + if (Texture::get_textures_power_2() != ATS_none) { x_size = up_to_power_2(width); y_size = up_to_power_2(height); } diff --git a/panda/src/grutil/movieTexture.cxx b/panda/src/grutil/movieTexture.cxx index 925c66fc06..fba3d285ab 100644 --- a/panda/src/grutil/movieTexture.cxx +++ b/panda/src/grutil/movieTexture.cxx @@ -236,7 +236,7 @@ recalculate_image_properties(CDWriter &cdata) { int x_size = x_max; int y_size = y_max; - if (textures_power_2 != ATS_none) { + if (Texture::get_textures_power_2() != ATS_none) { x_max = up_to_power_2(x_max); y_max = up_to_power_2(y_max); } diff --git a/panda/src/grutil/openCVTexture.cxx b/panda/src/grutil/openCVTexture.cxx index c1522f62d7..3fec6f6b21 100644 --- a/panda/src/grutil/openCVTexture.cxx +++ b/panda/src/grutil/openCVTexture.cxx @@ -156,7 +156,7 @@ reconsider_video_properties(const OpenCVTexture::VideoStream &stream, int x_size = width; int y_size = height; - if (textures_power_2 != ATS_none) { + if (Texture::get_textures_power_2() != ATS_none) { x_size = up_to_power_2(width); y_size = up_to_power_2(height); } diff --git a/panda/src/movies/movieVideoCursor.cxx b/panda/src/movies/movieVideoCursor.cxx index 2d5a65d76d..18d008e7da 100644 --- a/panda/src/movies/movieVideoCursor.cxx +++ b/panda/src/movies/movieVideoCursor.cxx @@ -86,7 +86,7 @@ void MovieVideoCursor:: setup_texture(Texture *tex) const { int fullx = size_x(); int fully = size_y(); - if (textures_power_2) { + if (Texture::get_textures_power_2()) { fullx = Texture::up_to_power_2(fullx); fully = Texture::up_to_power_2(fully); }