diff --git a/panda/src/display/config_display.cxx b/panda/src/display/config_display.cxx index e90f092a85..9118247e15 100644 --- a/panda/src/display/config_display.cxx +++ b/panda/src/display/config_display.cxx @@ -86,7 +86,7 @@ const bool show_buffers = config_display.GetBool("show-buffers", false); // buffer. This may be desired if you know your graphics API does not // support render-directly-to-texture and you want to minimize // framebuffer memory. -const bool prefer_parasite_buffer = config_display.GetBool("prefer-parasite-buffer", false); +const bool prefer_parasite_buffer = config_display.GetBool("prefer-parasite-buffer", true); // Set this true to make GraphicsOutput::make_render_texture() first // try to create a single-buffered offscreen buffer, before falling diff --git a/panda/src/display/graphicsEngine.cxx b/panda/src/display/graphicsEngine.cxx index 0fbe462398..be0dfa5b14 100644 --- a/panda/src/display/graphicsEngine.cxx +++ b/panda/src/display/graphicsEngine.cxx @@ -503,6 +503,50 @@ render_frame() { _app_pcollector.start(); } + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsEngine::open_windows +// Access: Published +// Description: Fully opens (or closes) any windows that have +// recently been requested open or closed, without +// rendering any frames. It is not necessary to call +// this explicitly, since windows will be automatically +// opened or closed when the next frame is rendered, but +// you may call this if you want your windows now +// without seeing a frame go by. +//////////////////////////////////////////////////////////////////// +void GraphicsEngine:: +open_windows() { + MutexHolder holder(_lock); + + if (!_windows_sorted) { + do_resort_windows(); + } + + _app.do_windows(this); + + Threads::const_iterator ti; + for (ti = _threads.begin(); ti != _threads.end(); ++ti) { + RenderThread *thread = (*ti).second; + if (thread->_thread_state == TS_wait) { + thread->_thread_state = TS_do_windows; + thread->_cv.signal(); + } + thread->_cv_mutex.release(); + } + + // We do it twice, to allow both cull and draw to process the + // window. + for (ti = _threads.begin(); ti != _threads.end(); ++ti) { + RenderThread *thread = (*ti).second; + if (thread->_thread_state == TS_wait) { + thread->_thread_state = TS_do_windows; + thread->_cv.signal(); + } + thread->_cv_mutex.release(); + } +} + //////////////////////////////////////////////////////////////////// // Function: GraphicsEngine::sync_frame // Access: Published @@ -747,6 +791,24 @@ cull_bin_draw(GraphicsStateGuardian *gsg, DisplayRegion *dr) { } } +//////////////////////////////////////////////////////////////////// +// Function: GraphicsEngine::make_contexts +// Access: Private +// Description: Called in the draw thread, this calls make_context() +// on each window on the list to guarantee its graphics +// context gets created. +//////////////////////////////////////////////////////////////////// +void GraphicsEngine:: +make_contexts(const GraphicsEngine::Windows &wlist) { + Windows::const_iterator wi; + for (wi = wlist.begin(); wi != wlist.end(); ++wi) { + GraphicsOutput *win = (*wi); + if (win->needs_context()) { + win->make_context(); + } + } +} + //////////////////////////////////////////////////////////////////// // Function: GraphicsEngine::process_events // Access: Private @@ -1321,6 +1383,25 @@ do_frame(GraphicsEngine *engine) { do_callbacks(CB_post_frame); } +//////////////////////////////////////////////////////////////////// +// Function: GraphicsEngine::WindowRenderer::do_windows +// Access: Public +// Description: Attempts to fully open or close any windows or +// buffers associated with this thread, but does not +// otherwise perform any rendering. (Normally, this +// step is handled during do_frame(); call this method +// only if you want these things to open immediately.) +//////////////////////////////////////////////////////////////////// +void GraphicsEngine::WindowRenderer:: +do_windows(GraphicsEngine *engine) { + MutexHolder holder(_wl_lock); + + engine->process_events(_window); + + engine->make_contexts(_cdraw); + engine->make_contexts(_draw); +} + //////////////////////////////////////////////////////////////////// // Function: GraphicsEngine::WindowRenderer::do_flip // Access: Public @@ -1542,6 +1623,13 @@ thread_main() { _thread_state = TS_wait; break; + case TS_do_windows: + do_windows(_engine); + do_pending(_engine); + do_release(_engine); + _thread_state = TS_wait; + break; + case TS_terminate: do_pending(_engine); do_close(_engine); diff --git a/panda/src/display/graphicsEngine.h b/panda/src/display/graphicsEngine.h index 44410fa5a8..92f3ffd743 100644 --- a/panda/src/display/graphicsEngine.h +++ b/panda/src/display/graphicsEngine.h @@ -88,6 +88,7 @@ PUBLISHED: bool is_empty() const; void render_frame(); + void open_windows(); void sync_frame(); void flip_frame(); @@ -100,6 +101,7 @@ public: TS_do_frame, TS_do_flip, TS_do_release, + TS_do_windows, TS_terminate }; @@ -139,6 +141,7 @@ private: void cull_bin_draw(const Windows &wlist); void cull_bin_draw(GraphicsStateGuardian *gsg, DisplayRegion *dr); + void make_contexts(const Windows &wlist); void process_events(const Windows &wlist); void flip_windows(const Windows &wlist); @@ -171,6 +174,7 @@ private: void remove_window(GraphicsOutput *window); void resort_windows(); void do_frame(GraphicsEngine *engine); + void do_windows(GraphicsEngine *engine); void do_flip(GraphicsEngine *engine); void do_release(GraphicsEngine *engine); void do_close(GraphicsEngine *engine); diff --git a/panda/src/display/graphicsOutput.I b/panda/src/display/graphicsOutput.I index 32577be690..ecb0a2a886 100644 --- a/panda/src/display/graphicsOutput.I +++ b/panda/src/display/graphicsOutput.I @@ -160,17 +160,6 @@ is_valid() const { return _is_valid; } -//////////////////////////////////////////////////////////////////// -// Function: GraphicsOutput::flip_ready -// Access: Published -// Description: Returns true if a frame has been rendered and needs -// to be flipped, false otherwise. -//////////////////////////////////////////////////////////////////// -INLINE bool GraphicsOutput:: -flip_ready() const { - return _flip_ready; -} - //////////////////////////////////////////////////////////////////// // Function: GraphicsOutput::get_sort // Access: Published @@ -243,6 +232,28 @@ win_display_regions_changed() { _display_regions_stale = true; } +//////////////////////////////////////////////////////////////////// +// Function: GraphicsOutput::flip_ready +// Access: Public +// Description: Returns true if a frame has been rendered and needs +// to be flipped, false otherwise. +//////////////////////////////////////////////////////////////////// +INLINE bool GraphicsOutput:: +flip_ready() const { + return _flip_ready; +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsOutput::needs_context +// Access: Public +// Description: Returns true if make_context() still needs to be +// called, false otherwise. +//////////////////////////////////////////////////////////////////// +INLINE bool GraphicsOutput:: +needs_context() const { + return _needs_context; +} + //////////////////////////////////////////////////////////////////// // Function: GraphicsOutput::operator < // Access: Public diff --git a/panda/src/display/graphicsOutput.cxx b/panda/src/display/graphicsOutput.cxx index 23030070b0..8820dd9df9 100644 --- a/panda/src/display/graphicsOutput.cxx +++ b/panda/src/display/graphicsOutput.cxx @@ -52,6 +52,7 @@ GraphicsOutput(GraphicsPipe *pipe, GraphicsStateGuardian *gsg, _is_valid = false; _copy_texture = false; _flip_ready = false; + _needs_context = true; _sort = 0; int mode = gsg->get_properties().get_frame_buffer_mode(); @@ -365,7 +366,15 @@ make_texture_buffer(const string &name, int x_size, int y_size) { if (sb_gsg != (GraphicsStateGuardian *)NULL) { buffer = engine->make_buffer(sb_gsg, name, sort, x_size, y_size, true); if (buffer != (GraphicsOutput *)NULL) { - return buffer; + // Check the buffer for goodness. + engine->open_windows(); + if (buffer->is_valid()) { + return buffer; + } + + // No good; delete the buffer and keep trying. + engine->remove_window(buffer); + buffer = (GraphicsOutput *)NULL; } } } @@ -376,7 +385,13 @@ make_texture_buffer(const string &name, int x_size, int y_size) { // source window is double-buffered. buffer = engine->make_buffer(gsg, name, sort, x_size, y_size, true); if (buffer != (GraphicsOutput *)NULL) { - return buffer; + engine->open_windows(); + if (buffer->is_valid()) { + return buffer; + } + + engine->remove_window(buffer); + buffer = (GraphicsOutput *)NULL; } // Looks like we have to settle for a parasite buffer. @@ -492,6 +507,12 @@ begin_frame() { return false; } + if (needs_context()) { + if (!make_context()) { + return false; + } + } + // Okay, we already have a GSG, so activate it. make_current(); return _gsg->begin_frame(); @@ -550,6 +571,22 @@ end_frame() { } } +//////////////////////////////////////////////////////////////////// +// Function: GraphicsOutput::make_context +// Access: Public, Virtual +// Description: If _needs_context is true, this will be called +// in the draw thread prior to rendering into the +// window. It should attempt to create a graphics +// context, and return true if successful, false +// otherwise. If it returns false the window will be +// considered failed. +//////////////////////////////////////////////////////////////////// +bool GraphicsOutput:: +make_context() { + _needs_context = false; + return true; +} + //////////////////////////////////////////////////////////////////// // Function: GraphicsOutput::make_current // Access: Public, Virtual diff --git a/panda/src/display/graphicsOutput.h b/panda/src/display/graphicsOutput.h index f96945881b..c256440bfd 100644 --- a/panda/src/display/graphicsOutput.h +++ b/panda/src/display/graphicsOutput.h @@ -80,7 +80,6 @@ PUBLISHED: INLINE int get_y_size() const; INLINE bool has_size() const; INLINE bool is_valid() const; - INLINE bool flip_ready() const; virtual bool is_active() const; @@ -109,6 +108,8 @@ public: public: // These are not intended to be called directly by the user. INLINE void win_display_regions_changed(); + INLINE bool needs_context() const; + INLINE bool flip_ready() const; INLINE bool operator < (const GraphicsOutput &other) const; @@ -129,6 +130,7 @@ public: // This method is called in the draw thread prior to issuing any // drawing commands for the window. + virtual bool make_context(); virtual void make_current(); virtual void release_gsg(); @@ -151,6 +153,7 @@ protected: PT(Texture) _texture; bool _copy_texture; bool _flip_ready; + bool _needs_context; private: INLINE void determine_display_regions() const; diff --git a/panda/src/display/parasiteBuffer.cxx b/panda/src/display/parasiteBuffer.cxx index 25d5a97565..b905796fa6 100644 --- a/panda/src/display/parasiteBuffer.cxx +++ b/panda/src/display/parasiteBuffer.cxx @@ -64,6 +64,7 @@ ParasiteBuffer(GraphicsOutput *host, const string &name, //////////////////////////////////////////////////////////////////// ParasiteBuffer:: ~ParasiteBuffer() { + _is_valid = false; } //////////////////////////////////////////////////////////////////// diff --git a/panda/src/glstuff/glGraphicsStateGuardian_src.I b/panda/src/glstuff/glGraphicsStateGuardian_src.I index 4fe949d653..8b4698edb9 100644 --- a/panda/src/glstuff/glGraphicsStateGuardian_src.I +++ b/panda/src/glstuff/glGraphicsStateGuardian_src.I @@ -615,14 +615,14 @@ enable_multisample(bool val) { if (_multisample_enabled != val) { _multisample_enabled = val; if (val) { -#ifdef GL_MULTISAMPLE_SGIS - GLP(Enable)(GL_MULTISAMPLE_SGIS); -#endif + if (_supports_multisample) { + GLP(Enable)(GL_MULTISAMPLE); + } GLP(Hint)(GL_POINT_SMOOTH_HINT, GL_NICEST); } else { -#ifdef GL_MULTISAMPLE_SGIS - GLP(Disable)(GL_MULTISAMPLE_SGIS); -#endif + if (_supports_multisample) { + GLP(Disable)(GL_MULTISAMPLE); + } GLP(Hint)(GL_POINT_SMOOTH_HINT, GL_FASTEST); } } diff --git a/panda/src/glstuff/glGraphicsStateGuardian_src.cxx b/panda/src/glstuff/glGraphicsStateGuardian_src.cxx index f8892198aa..26a078e8f2 100644 --- a/panda/src/glstuff/glGraphicsStateGuardian_src.cxx +++ b/panda/src/glstuff/glGraphicsStateGuardian_src.cxx @@ -380,6 +380,8 @@ reset() { _current_projection_mat = LMatrix4f::ident_mat(); _projection_mat_stack_count = 0; + report_my_gl_errors(); + // Make sure the GL state matches all of our initial attribute // states. CPT(RenderAttrib) dta = DepthTestAttrib::make(DepthTestAttrib::M_less); @@ -419,7 +421,7 @@ reset() { // Output the vendor and version strings. show_gl_string("GL_VENDOR", GL_VENDOR); show_gl_string("GL_RENDERER", GL_RENDERER); - show_gl_string("GL_VERSION", GL_VERSION); + get_gl_version(); // Save the extensions tokens. save_extensions((const char *)glGetString(GL_EXTENSIONS)); @@ -427,6 +429,14 @@ reset() { report_extensions(); _supports_bgr = has_extension("GL_EXT_bgra"); + _supports_multisample = has_extension("GL_ARB_multisample"); + + _edge_clamp = GL_CLAMP; + if (has_extension("GL_SGIS_texture_edge_clamp") || + is_at_least_version(1, 2)) { + _edge_clamp = GL_CLAMP_TO_EDGE; + } + _error_count = 0; report_my_gl_errors(); @@ -2663,6 +2673,47 @@ show_gl_string(const string &name, GLenum id) { } } +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::get_gl_version +// Access: Protected +// Description: Queries the runtime version of OpenGL in use. +//////////////////////////////////////////////////////////////////// +void CLP(GraphicsStateGuardian):: +get_gl_version() { + _gl_version_major = 0; + _gl_version_minor = 0; + _gl_version_release = 0; + + const GLubyte *text = GLP(GetString)(GL_VERSION); + if (text == (const GLubyte *)NULL) { + GLCAT.debug() + << "Unable to query GL_VERSION\n"; + } else { + string input((const char *)text); + size_t space = input.find(' '); + if (space != string::npos) { + input = input.substr(0, space); + } + + vector_string components; + tokenize(input, components, "."); + if (components.size() >= 1) { + string_to_int(components[0], _gl_version_major); + } + if (components.size() >= 2) { + string_to_int(components[1], _gl_version_minor); + } + if (components.size() >= 3) { + string_to_int(components[2], _gl_version_release); + } + + GLCAT.debug() + << "GL_VERSION = " << (const char *)text << ", decoded to " + << _gl_version_major << "." << _gl_version_minor + << "." << _gl_version_release << "\n"; + } +} + //////////////////////////////////////////////////////////////////// // Function: GLGraphicsStateGuardian::save_extensions // Access: Protected @@ -2725,6 +2776,26 @@ has_extension(const string &extension) const { return (_extensions.find(extension) != _extensions.end()); } +//////////////////////////////////////////////////////////////////// +// Function: CLP(GraphicsStateGuardian)::is_at_least_version +// Access: Public +// Description: Returns true if the runtime GL version number is at +// least the indicated value, false otherwise. +//////////////////////////////////////////////////////////////////// +bool CLP(GraphicsStateGuardian):: +is_at_least_version(int major, int minor, int release) const { + if (_gl_version_major < major) { + return false; + } + if (_gl_version_minor < minor) { + return false; + } + if (_gl_version_release < release) { + return false; + } + return true; +} + //////////////////////////////////////////////////////////////////// // Function: CLP(GraphicsStateGuardian)::set_draw_buffer @@ -2918,12 +2989,10 @@ compute_gl_image_size(int xsize, int ysize, int external_format, int type) { pixel_width = 2 * num_components; break; -#ifdef GL_UNSIGNED_BYTE_3_3_2_EXT - case GL_UNSIGNED_BYTE_3_3_2_EXT: + case GL_UNSIGNED_BYTE_3_3_2: nassertr(num_components == 3, 0); pixel_width = 1; break; -#endif case GL_FLOAT: pixel_width = 4 * num_components; @@ -3255,7 +3324,7 @@ get_texture_wrap_mode(Texture::WrapMode wm) { } switch (wm) { case Texture::WM_clamp: - return GL_CLAMP; + return _edge_clamp; case Texture::WM_repeat: return GL_REPEAT; @@ -3269,7 +3338,7 @@ get_texture_wrap_mode(Texture::WrapMode wm) { break; } GLCAT.error() << "Invalid Texture::WrapMode value!\n"; - return GL_CLAMP; + return _edge_clamp; } //////////////////////////////////////////////////////////////////// @@ -3332,10 +3401,8 @@ get_image_type(PixelBuffer::Type type) { return GL_UNSIGNED_BYTE; case PixelBuffer::T_unsigned_short: return GL_UNSIGNED_SHORT; -#ifdef GL_UNSIGNED_BYTE_3_3_2_EXT case PixelBuffer::T_unsigned_byte_332: - return GL_UNSIGNED_BYTE_3_3_2_EXT; -#endif + return GL_UNSIGNED_BYTE_3_3_2; case PixelBuffer::T_float: return GL_FLOAT; @@ -3480,9 +3547,7 @@ get_fog_mode_type(Fog::Mode m) const { case Fog::M_exponential: return GL_EXP; case Fog::M_exponential_squared: return GL_EXP2; /* - #ifdef GL_FOG_FUNC_SGIS case Fog::M_spline: return GL_FOG_FUNC_SGIS; - #endif */ default: @@ -3525,13 +3590,10 @@ print_gfx_visual() { GLP(GetBooleanv)( GL_STEREO, &j ); cout << "Stereo? " << (int)j << endl; -#ifdef GL_MULTISAMPLE_SGIS - GLP(GetBooleanv)( GL_MULTISAMPLE_SGIS, &j ); cout << "Multisample? " - << (int)j << endl; -#endif -#ifdef GL_SAMPLES_SGIS - GLP(GetIntegerv)( GL_SAMPLES_SGIS, &i ); cout << "Samples: " << i << endl; -#endif + if (_supports_multisample) { + GLP(GetBooleanv)( GL_MULTISAMPLE, &j ); cout << "Multisample? " << (int)j << endl; + GLP(GetIntegerv)( GL_SAMPLES, &i ); cout << "Samples: " << i << endl; + } GLP(GetBooleanv)( GL_BLEND, &j ); cout << "Blend? " << (int)j << endl; GLP(GetBooleanv)( GL_POINT_SMOOTH, &j ); cout << "Point Smooth? " diff --git a/panda/src/glstuff/glGraphicsStateGuardian_src.h b/panda/src/glstuff/glGraphicsStateGuardian_src.h index 2bc2730d9d..b4462a750b 100644 --- a/panda/src/glstuff/glGraphicsStateGuardian_src.h +++ b/panda/src/glstuff/glGraphicsStateGuardian_src.h @@ -133,10 +133,12 @@ protected: static bool report_errors_loop(int line, const char *source_file, GLenum error_code, int &error_count); void show_gl_string(const string &name, GLenum id); + void get_gl_version(); void save_extensions(const char *extensions); virtual void get_extra_extensions(); void report_extensions() const; bool has_extension(const string &extension) const; + bool is_at_least_version(int major, int minor, int release = 0) const; virtual bool slot_new_light(int light_id); virtual void enable_lighting(bool enable); @@ -299,8 +301,11 @@ protected: int _pass_number; + int _gl_version_major, _gl_version_minor, _gl_version_release; pset _extensions; bool _supports_bgr; + bool _supports_multisample; + GLenum _edge_clamp; int _error_count; diff --git a/panda/src/wgldisplay/wglGraphicsBuffer.cxx b/panda/src/wgldisplay/wglGraphicsBuffer.cxx index 510763f597..9c3e124f28 100644 --- a/panda/src/wgldisplay/wglGraphicsBuffer.cxx +++ b/panda/src/wgldisplay/wglGraphicsBuffer.cxx @@ -152,6 +152,45 @@ end_frame() { } } +//////////////////////////////////////////////////////////////////// +// Function: wglGraphicsBuffer::make_context +// Access: Public, Virtual +// Description: If _needs_context is true, this will be called +// in the draw thread prior to rendering into the +// window. It should attempt to create a graphics +// context, and return true if successful, false +// otherwise. If it returns false the window will be +// considered failed. +//////////////////////////////////////////////////////////////////// +bool wglGraphicsBuffer:: +make_context() { + PStatTimer timer(_make_current_pcollector); + + wglGraphicsStateGuardian *wglgsg; + DCAST_INTO_R(wglgsg, _gsg, false); + + if (_pbuffer_dc) { + HGLRC context = wglgsg->get_context(_pbuffer_dc); + if (context) { + wglMakeCurrent(_pbuffer_dc, context); + wglgsg->reset_if_new(); + _needs_context = false; + return true; + } + + } else { + HGLRC context = wglgsg->get_context(_window_dc); + if (context) { + wglMakeCurrent(_window_dc, context); + wglgsg->reset_if_new(); + _needs_context = false; + return true; + } + } + + return false; +} + //////////////////////////////////////////////////////////////////// // Function: wglGraphicsBuffer::make_current // Access: Public, Virtual diff --git a/panda/src/wgldisplay/wglGraphicsBuffer.h b/panda/src/wgldisplay/wglGraphicsBuffer.h index 0b0af8132d..12ac4922ee 100644 --- a/panda/src/wgldisplay/wglGraphicsBuffer.h +++ b/panda/src/wgldisplay/wglGraphicsBuffer.h @@ -49,6 +49,7 @@ public: virtual bool begin_frame(); virtual void end_frame(); + virtual bool make_context(); virtual void make_current(); virtual void release_gsg(); diff --git a/panda/src/wgldisplay/wglGraphicsWindow.cxx b/panda/src/wgldisplay/wglGraphicsWindow.cxx index 9eb17eb8ab..a599eb4177 100644 --- a/panda/src/wgldisplay/wglGraphicsWindow.cxx +++ b/panda/src/wgldisplay/wglGraphicsWindow.cxx @@ -147,6 +147,33 @@ wglGraphicsWindow:: ~wglGraphicsWindow() { } +//////////////////////////////////////////////////////////////////// +// Function: wglGraphicsWindow::make_context +// Access: Public, Virtual +// Description: If _needs_context is true, this will be called +// in the draw thread prior to rendering into the +// window. It should attempt to create a graphics +// context, and return true if successful, false +// otherwise. If it returns false the window will be +// considered failed. +//////////////////////////////////////////////////////////////////// +bool wglGraphicsWindow:: +make_context() { + PStatTimer timer(_make_current_pcollector); + + wglGraphicsStateGuardian *wglgsg; + DCAST_INTO_R(wglgsg, _gsg, false); + + HGLRC context = wglgsg->get_context(_hdc); + if (context) { + wglMakeCurrent(_hdc, context); + wglgsg->reset_if_new(); + _needs_context = false; + return true; + } + return false; +} + //////////////////////////////////////////////////////////////////// // Function: wglGraphicsWindow::make_current // Access: Public, Virtual @@ -161,15 +188,8 @@ make_current() { wglGraphicsStateGuardian *wglgsg; DCAST_INTO_V(wglgsg, _gsg); HGLRC context = wglgsg->get_context(_hdc); - if (context) { - wglMakeCurrent(_hdc, context); - - // Now that we have made the context current to a window, we can - // reset the GSG state if this is the first time it has been used. - // (We can't just call reset() when we construct the GSG, because - // reset() requires having a current context.) - wglgsg->reset_if_new(); - } + nassertv(context); + wglMakeCurrent(_hdc, context); } //////////////////////////////////////////////////////////////////// diff --git a/panda/src/wgldisplay/wglGraphicsWindow.h b/panda/src/wgldisplay/wglGraphicsWindow.h index 2a73cbaa61..bb49f9368a 100644 --- a/panda/src/wgldisplay/wglGraphicsWindow.h +++ b/panda/src/wgldisplay/wglGraphicsWindow.h @@ -33,6 +33,7 @@ public: const string &name); virtual ~wglGraphicsWindow(); + virtual bool make_context(); virtual void make_current(); virtual void release_gsg();