diff --git a/panda/src/display/config_display.cxx b/panda/src/display/config_display.cxx index 04a3a9c397..e90f092a85 100644 --- a/panda/src/display/config_display.cxx +++ b/panda/src/display/config_display.cxx @@ -81,6 +81,24 @@ const string screenshot_extension = config_display.GetString("screenshot-extensi // interactively. Handy during development of multipass algorithms. const bool show_buffers = config_display.GetBool("show-buffers", false); +// Set this true to make GraphicsOutput::make_render_texture() try to +// create a parasite buffer before it tries to create an offscreen +// 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); + +// Set this true to make GraphicsOutput::make_render_texture() first +// try to create a single-buffered offscreen buffer, before falling +// back to a double-buffered one (or whatever kind the source window +// has). This is true by default to reduce waste of framebuffer +// memory, but you may get a performance benefit by setting it to +// false (since in that case the buffer can share a graphics context +// with the window). +const bool prefer_single_buffer = config_display.GetBool("prefer-single-buffer", true); + + + // Use the variable load-display to specify the name of the default // graphics display library or GraphicsPipe to load. It is the name // of a shared library (or * for all libraries named in aux-display), diff --git a/panda/src/display/config_display.h b/panda/src/display/config_display.h index e46156a210..1cc7cd50fe 100644 --- a/panda/src/display/config_display.h +++ b/panda/src/display/config_display.h @@ -42,6 +42,9 @@ extern const string screenshot_extension; extern const bool show_buffers; +extern const bool prefer_parasite_buffer; +extern const bool prefer_single_buffer; + extern EXPCL_PANDA const bool multiple_windows; extern EXPCL_PANDA void init_libdisplay(); diff --git a/panda/src/display/graphicsOutput.cxx b/panda/src/display/graphicsOutput.cxx index 5d95c216ee..8ebe365598 100644 --- a/panda/src/display/graphicsOutput.cxx +++ b/panda/src/display/graphicsOutput.cxx @@ -298,6 +298,94 @@ get_display_region(int n) const { return result; } +//////////////////////////////////////////////////////////////////// +// Function: GraphicsOutput::make_texture_buffer +// Access: Published +// Description: Creates and returns an offscreen buffer for rendering +// into, the result of which will be a texture suitable +// for applying to geometry within the scene rendered +// into this window. +// +// This will attempt to be smart about maximizing render +// performance while minimizing framebuffer waste. It +// might return a GraphicsBuffer set to render directly +// into a texture, if possible; or it might return a +// ParasiteBuffer that renders into this window. The +// return value is NULL if the buffer could not be +// created for some reason. +// +// Assuming the return value is not NULL, the texture +// that is represents the scene rendered to the new +// buffer can be accessed by buffer->get_texture(). +// When you are done using the buffer, you should remove +// it with a call to GraphicsEngine::remove_window(). +//////////////////////////////////////////////////////////////////// +GraphicsOutput *GraphicsOutput:: +make_texture_buffer(const string &name, int x_size, int y_size) { + GraphicsStateGuardian *gsg = get_gsg(); + GraphicsEngine *engine = gsg->get_engine(); + GraphicsOutput *host = get_host(); + + // The new buffer should be drawn before this buffer is drawn. If + // the user requires more control than this, he can set the sort + // value himself. + int sort = get_sort() - 1; + + if (show_buffers) { + // If show_buffers is true, just go ahead and call make_buffer(), + // since it all amounts to the same thing anyway--this will + // actually create a new GraphicsWindow. + return engine->make_buffer(gsg, name, sort, x_size, y_size, true); + } + + GraphicsOutput *buffer = NULL; + + // If the user so indicated in the Configrc file, try to create a + // parasite buffer first. We can only do this if the requested size + // fits within the available framebuffer size. + if (prefer_parasite_buffer && + (x_size <= host->get_x_size() && y_size <= host->get_y_size())) { + buffer = engine->make_parasite(host, name, sort, x_size, y_size); + if (buffer != (GraphicsOutput *)NULL) { + return buffer; + } + } + + // Attempt to create a single-buffered offscreen buffer. + if (prefer_single_buffer) { + FrameBufferProperties sb_props = gsg->get_properties(); + int orig_mode = sb_props.get_frame_buffer_mode(); + int sb_mode = (orig_mode & ~FrameBufferProperties::FM_buffer) | FrameBufferProperties::FM_single_buffer; + sb_props.set_frame_buffer_mode(sb_mode); + + if (sb_mode != orig_mode) { + PT(GraphicsStateGuardian) sb_gsg = + engine->make_gsg(gsg->get_pipe(), sb_props, gsg); + if (sb_gsg != (GraphicsStateGuardian *)NULL) { + buffer = engine->make_buffer(sb_gsg, name, sort, x_size, y_size, true); + if (buffer != (GraphicsOutput *)NULL) { + return buffer; + } + } + } + } + + // All right, attempt to create an offscreen buffer, using the same + // GSG. This will be a double-buffered offscreen buffer, if the + // source window is double-buffered. + buffer = engine->make_buffer(gsg, name, sort, x_size, y_size, true); + if (buffer != (GraphicsOutput *)NULL) { + return buffer; + } + + // Looks like we have to settle for a parasite buffer. + if (x_size <= host->get_x_size() && y_size <= host->get_y_size()) { + return engine->make_parasite(host, name, sort, x_size, y_size); + } + + return NULL; +} + //////////////////////////////////////////////////////////////////// // Function: GraphicsOutput::make_scratch_display_region // Access: Public @@ -325,6 +413,20 @@ make_scratch_display_region(int x_size, int y_size) { region->copy_clear_settings(*this); return region; } + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsOutput::get_host +// Access: Public, Virtual +// Description: This is normally called only from within +// make_texture_buffer(). When called on a +// ParasiteBuffer, it returns the host of that buffer; +// but when called on some other buffer, it returns the +// buffer itself. +//////////////////////////////////////////////////////////////////// +GraphicsOutput *GraphicsOutput:: +get_host() { + return this; +} //////////////////////////////////////////////////////////////////// // Function: GraphicsOutput::request_open diff --git a/panda/src/display/graphicsOutput.h b/panda/src/display/graphicsOutput.h index 444d8723c2..9788f11f6a 100644 --- a/panda/src/display/graphicsOutput.h +++ b/panda/src/display/graphicsOutput.h @@ -95,6 +95,8 @@ PUBLISHED: int get_num_display_regions() const; DisplayRegion *get_display_region(int n) const; + GraphicsOutput *make_texture_buffer(const string &name, int x_size, int y_size); + INLINE Filename save_screenshot_default(const string &prefix = "screenshot"); INLINE bool save_screenshot(const Filename &filename); INLINE bool get_screenshot(PNMImage &image); @@ -109,6 +111,8 @@ public: INLINE bool operator < (const GraphicsOutput &other) const; + virtual GraphicsOutput *get_host(); + virtual void request_open(); virtual void request_close(); diff --git a/panda/src/display/parasiteBuffer.cxx b/panda/src/display/parasiteBuffer.cxx index aaded1ad1c..25d5a97565 100644 --- a/panda/src/display/parasiteBuffer.cxx +++ b/panda/src/display/parasiteBuffer.cxx @@ -54,7 +54,7 @@ ParasiteBuffer(GraphicsOutput *host, const string &name, _is_valid = true; - nassertv(_x_size < host->get_x_size() && _y_size < host->get_y_size()); + nassertv(_x_size <= host->get_x_size() && _y_size <= host->get_y_size()); } //////////////////////////////////////////////////////////////////// @@ -77,6 +77,20 @@ is_active() const { return _host->is_active(); } +//////////////////////////////////////////////////////////////////// +// Function: ParasiteBuffer::get_host +// Access: Public, Virtual +// Description: This is normally called only from within +// make_texture_buffer(). When called on a +// ParasiteBuffer, it returns the host of that buffer; +// but when called on some other buffer, it returns the +// buffer itself. +//////////////////////////////////////////////////////////////////// +GraphicsOutput *ParasiteBuffer:: +get_host() { + return _host; +} + //////////////////////////////////////////////////////////////////// // Function: ParasiteBuffer::make_current // Access: Public, Virtual diff --git a/panda/src/display/parasiteBuffer.h b/panda/src/display/parasiteBuffer.h index 873ce3f369..7dd3e20871 100644 --- a/panda/src/display/parasiteBuffer.h +++ b/panda/src/display/parasiteBuffer.h @@ -63,6 +63,7 @@ PUBLISHED: virtual bool is_active() const; public: + virtual GraphicsOutput *get_host(); virtual void make_current(); private: