diff --git a/panda/src/glstuff/glGraphicsBuffer_src.cxx b/panda/src/glstuff/glGraphicsBuffer_src.cxx index 5ffbb054bf..c8aebba355 100644 --- a/panda/src/glstuff/glGraphicsBuffer_src.cxx +++ b/panda/src/glstuff/glGraphicsBuffer_src.cxx @@ -24,14 +24,32 @@ TypeHandle CLP(GraphicsBuffer)::_type_handle; // Description: //////////////////////////////////////////////////////////////////// CLP(GraphicsBuffer):: -CLP(GraphicsBuffer)(GraphicsPipe *pipe, GraphicsStateGuardian *gsg, - const string &name, - int x_size, int y_size) : - GraphicsBuffer(pipe, gsg, name, x_size, y_size) +CLP(GraphicsBuffer)(GraphicsPipe *pipe, + const string &name, + int x_size, int y_size, int flags, + GraphicsStateGuardian *gsg, + GraphicsOutput *host) : + GraphicsBuffer(pipe, name, x_size, y_size, flags, gsg, host) { // Since the FBO never gets flipped, we get screenshots from the // same buffer we draw into. _screenshot_buffer_type = _draw_buffer_type; + + // Initialize these. + _fbo = 0; + _rb_size_x = 0; + _rb_size_y = 0; + for (int i=0; ibegin_frame(FM_parasite)) { return false; } + + // Figure out the desired size of the buffer. + if (mode == FM_render) { + rebuild_bitplanes(); + clear_cube_map_selection(); + } +} - if (_track_host_size) { +//////////////////////////////////////////////////////////////////// +// Function: glGraphicsBuffer::rebuild_bitplanes +// Access: Public, Virtual +// Description: This function will be called within the draw thread +// to allocate/reallocate the fbo and all the associated +// renderbuffers, just before rendering a frame. +//////////////////////////////////////////////////////////////////// +void glGraphicsBuffer:: +rebuild_bitplanes() { + + glGraphicsStateGuardian *glgsg; + DCAST_INTO_R(glgsg, _gsg, false); + + // Bind the FBO + + if (_fbo == 0) { + glgsg->_glGenFramebuffersEXT(1, &_fbo); + if (_fbo == 0) { + glgsg->report_my_gl_errors(); + return; + } + } + glgsg->_glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, _fbo); + + // Calculate bitplane size. This can be larger than the buffer. + + if (_creation_flags & GraphicsPipe::BF_track_host_size) { if ((_host->get_x_size() != _x_size)|| (_host->get_y_size() != _y_size)) { set_size_and_recalc(_host->get_x_size(), _host->get_y_size()); } } + int desired_x = _x_size; + int desired_y = _y_size; + if (!glgsg->get_supports_tex_non_pow2()) { + desired_x = Texture::up_to_power_2(desired_x); + desired_y = Texture::up_to_power_2(desired_y); + } - // This function also contains the code to *reopen* the window - // in the event that configuration parameters (ie, size, texture - // bindings) have changed. - open_window(); + // Scan the textures list and determine what should be attached. - // bind the FBO + Texture *attach[SLOT_COUNT]; + for (int i=0; iget_format(); - if (mode == FM_render) { - begin_render_texture(); - clear_cube_map_selection(); + // If it's a not a 2D texture or a cube map, punt it. + if ((tex->get_texture_type() != Texture::TT_2d_texture)&& + (tex->get_texture_type() != Texture::TT_cube_map)) { + _rtm_mode[i] = RTM_copy_texture; + continue; + } + + // Identify right attachment point. + + int slot = SLOT_COUNT; + if (fmt == Texture::F_depth_component) { + slot = SLOT_depth; + } else if (fmt == Texture::F_stencil_index) { + slot = SLOT_stencil; + } else if (fmt == Texture::F_rgba) { + slot = SLOT_color; + } else { + _rtm_mode[i] = RTM_copy_texture; + continue; + } + + // If there's already a texture bound to this slot, + // then punt this texture. + if (attach[slot]) { + _rtm_mode[i] = RTM_copy_texture; + continue; + } + + // Assign the texture to this slot. + attach[slot] = tex; + } + + + // For all slots, update the slot. + + for (int slot=0; slotget_x_size() == desired_x)&& + (tex->get_y_size() == desired_y)) { + continue; + } + + // Bind the texture to the slot. + tex->set_x_size(desired_x); + tex->set_y_size(desired_y); + TextureContext *tc = tex->prepare_now(get_prepared_objects(), this); + nassertv(tc != (TextureContext *)NULL); + CLP(TextureContext) *gtc = DCAST(CLP(TextureContext), tc); + + if (tex->get_texture_type() == Texture::TT_2d_texture) { + glgsg->_glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, _attach_point[slot], + GL_TEXTURE_2D, gtc->_index, 0); + } else { + glgsg->_glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, _attach_point[slot], + GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB, gtc->_index, 0); + } + _tex[slot] = tex; + + // If there was a renderbuffer bound to this slot, delete it. + if (_rb[slot] != 0) { + glgsg->_glDeleteRenderbuffers(1, &(_rb[slot])); + _rb[slot] = 0; + } + + } else { + + // If a renderbuffer is already attached to the slot, and it's + // the right size, then no update of this slot is needed. + if ((_rb[slot] != 0)&& + (_rb_size_x == desired_x)&& + (_rb_size_y == desired_y)) { + continue; + } + + // If there's no renderbuffer for this slot, create one. + if (_rb[slot] == 0) { + glgsg->_glGenRenderbuffers(1, &(_rb[slot])); + } + + // Resize the renderbuffer appropriately. + glgsg->_glBindRenderbuffer(GL_RENDERBUFFER_EXT, _rb[slot]); + glgsg->_glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, _slot_format[slot], + desired_x, desired_y); + glgsg->_glBindRenderbuffer(GL_RENDERBUFFER_EXT, 0); + + // Bind the renderbuffer to the slot. + glgsg->_glFramebufferRenderbuffer(GL_FRAMEBUFFER_EXT, _attach_point[slot], + GL_RENDERBUFFER_EXT, _rb[slot]); + + // Toss any texture that was connected to the slot. + _tex[slot] = 0; + } + } + + // These record the size of all nonzero renderbuffers. + _rb_size_x = desired_x; + _rb_size_y = desired_y; +} + +//////////////////////////////////////////////////////////////////// +// Function: glGraphicsBuffer::generate_mipmaps +// Access: Private +// Description: This function will be called within the draw thread +// after rendering is completed for a given frame. +// If we've just rendered into level zero of a mipmapped +// texture, then all subsequent mipmap levels will now +// be calculated. +//////////////////////////////////////////////////////////////////// +void glGraphicsBuffer:: +generate_mipmaps() { + glGraphicsStateGuardian *glgsg; + DCAST_INTO_R(glgsg, _gsg, false); + + for (int slot=0; slotuses_mipmaps())) { + glgsg->_state._texture = 0; + TextureContext *tc = tex->prepare_now(get_prepared_objects(), this); + nassert(tc != (TextureContext *)NULL); + CLP(TextureContext) *gtc = DCAST(CLP(TextureContext), tc); + GLenum target = get_texture_target(tex->get_texture_type()); + GLP(BindTexture)(target, gtc->_index); + glgsg->_glGenerateMipmap(target); + GLP(BindTexture)(target, 0); + } } } //////////////////////////////////////////////////////////////////// -// Function: wglGraphicsBuffer::end_frame +// Function: glGraphicsBuffer::end_frame // Access: Public, Virtual // Description: This function will be called within the draw thread // after rendering is completed for a given frame. It // should do whatever finalization is required. //////////////////////////////////////////////////////////////////// -void wglGraphicsBuffer:: +void glGraphicsBuffer:: end_frame(FrameMode mode) { end_frame_spam(); nassertv(_gsg != (GraphicsStateGuardian *)NULL); if (mode == FM_render) { - end_render_texture(); copy_to_textures(); } // Unbind the FBO + glgsg->_glBindFramebuffer(GL_FRAMEBUFFER_EXT, 0); + if (mode == FM_render) { + generate_mipmaps(); + } + _host->end_frame(FM_parasite); if (mode == FM_render) { @@ -127,68 +318,58 @@ end_frame(FrameMode mode) { //////////////////////////////////////////////////////////////////// void CLP(GraphicsBuffer):: select_cube_map(int cube_map_index) { - open_buffer(); + GLCAT.error() << "select_cube_map not implemented yet.\n"; } //////////////////////////////////////////////////////////////////// -// Function: wglGraphicsBuffer::open_buffer +// Function: glGraphicsBuffer::open_buffer // Access: Protected, Virtual // Description: Opens the window right now. Called from the window // thread. Returns true if the window is successfully // opened, or false if there was a problem. -// -// This particular version of open_buffer is also -// capable of reopening the buffer, which is necessary -// if the texture bindings, size, or cube face has -// changed. Caution: since this function is called -// at every cube-map switch, it needs to be reasonably -// fast in the case that little is changing. //////////////////////////////////////////////////////////////////// -bool wglGraphicsBuffer:: +bool glGraphicsBuffer:: open_buffer() { - // Make sure the FBO is allocated. - - // Figure out the desired size of the FBO - int desired_x = _x_size; - int desired_y = _y_size; - if (!_gsg->get_supports_tex_non_pow2()) { - desired_x = Texture::up_to_power_2(desired_x); - desired_y = Texture::up_to_power_2(desired_y); + + // Check for support of relevant extensions. + glGraphicsStateGuardian *glgsg; + DCAST_INTO_R(glgsg, _gsg, false); + if ((!glgsg->_supports_framebuffer_object)|| + (glgsg->_glDrawBuffers == 0)) { + return false; } - // Scan the textures list to see which textures should be attached. - Texture *attach_color = 0; - Texture *attach_depth = 0; - Texture *attach_stencil = 0; - - - // For all slots containing textures, detach if: - // - they aren't supposed to be attached any more, or - // - there is a size mismatch, or - // - there is a cube-face mismatch - - // For all renderbuffers, detach and deallocate if: - // - there is a size mismatch, or, - // - there is a texture to be attached at that point - - // For all to-be-attached textures, attach if not already - // attached. During attachment process, resize and reformat - // if needed. - - // For all unfilled slots, allocate and attach render buffers. + _is_valid = true; + return true; } //////////////////////////////////////////////////////////////////// -// Function: wglGraphicsBuffer::close_buffer +// Function: glGraphicsBuffer::close_buffer // Access: Protected, Virtual // Description: Closes the buffer right now. Called from the window // thread. //////////////////////////////////////////////////////////////////// -void wglGraphicsBuffer:: +void glGraphicsBuffer:: close_buffer() { - // Detach all renderbuffers - // Detach all textures - // Deallocate the FBO itself. + + // Get the glgsg. + glGraphicsStateGuardian *glgsg; + DCAST_INTO_R(glgsg, _gsg, false); + + // Delete the renderbuffers. + for (int i=0; i_glDeleteRenderbuffersEXT(1, &(_rb[i])); + _rb[i] = 0; + } + _tex[i] = 0; + } + _rb_size_x = 0; + _rb_size_y = 0; + + // Delete the FBO itself. + nassertv(_fbo != 0, false); + glgsg->_glDeleteFramebuffersEXT(1, &_fbo); } //////////////////////////////////////////////////////////////////// diff --git a/panda/src/glstuff/glGraphicsBuffer_src.h b/panda/src/glstuff/glGraphicsBuffer_src.h index 30994ea76b..73258b56e2 100644 --- a/panda/src/glstuff/glGraphicsBuffer_src.h +++ b/panda/src/glstuff/glGraphicsBuffer_src.h @@ -24,38 +24,38 @@ // Class : glGraphicsBuffer // Description : An offscreen render buffer. // -// The glGraphicsBuffer can export its color buffer as a -// texture. It can also export its depth buffer as a depth -// texture and its stencil buffer as a stencil texture. -// Finally, it can have auxiliary buffers (additional -// bitplanes above and beyond the usual set), which can -// also be exported as textures. This is the key advantage -// of the glGraphicsBuffer: it can render to many textures -// at the same time. +// The glGraphicsBuffer is based on the OpenGL +// EXT_framebuffer_object and ARB_draw_buffers extensions. +// This design has three significant advantages over the +// wglGraphicsBuffer and glxGraphicsBuffer. // -// The glGraphicsBuffer shares a gsg with a host window. -// If the host window is destroyed, the glGraphicsBuffer is -// lost as well. If desired, the glGraphicsBuffer can -// track the size of the host window. +// As you might expect, this type of buffer can export +// its color buffer as a texture. But it can also export +// its depth buffer, its stencil buffer, and any number +// of auxiliary buffers. This is the biggest advantage: +// it can render to many textures at the same time. // -// The glGraphicsBuffer is implemented using the following -// OpenGL extensions: -// -// EXT_framebuffer_object -// ARB_draw_buffers +// There is also a speed advantage. When using a +// glGraphicsBuffer, it is not necessary to call the +// extremely expensive wglMakeCurrent on buffer switches. // -// If any of these extensions is missing, then -// glGraphicsBuffer is not available (although it may -// still be possible to create a wglGraphicsBuffer or -// glxGraphicsBuffer). +// The glGraphicsBuffer can also track the size of a host +// window, and automatically resize itself to match. +// +// If either of the necessary OpenGL extensions is not +// available, then the glGraphicsBuffer will not be +// available (although it may still be possible to +// create a wglGraphicsBuffer or glxGraphicsBuffer). // //////////////////////////////////////////////////////////////////// class EXPCL_GL CLP(GraphicsBuffer) : public GraphicsBuffer { public: - CLP(GraphicsBuffer)(GraphicsPipe *pipe, GraphicsStateGuardian *gsg, + CLP(GraphicsBuffer)(GraphicsPipe *pipe, const string &name, - int x_size, int y_size); + int x_size, int y_size, int flags, + GraphicsStateGuardian *gsg, + GraphicsOutput *host); virtual ~CLP(GraphicsBuffer)(); virtual bool begin_frame(FrameMode mode); @@ -68,20 +68,25 @@ protected: virtual bool open_buffer(); private: - PT(GraphicsOutput) _host; - bool _track_host_size; - int _fbo; - int _fbo_size_x; - int _fbo_size_y; - int _attached_cube_face; - Texture *_attached_color; - Texture *_attached_depth; - Texture *_attached_stencil; - int _attached_color_rb; - int _attached_depth_rb; - int _attached_stencil_rb; + void generate_mipmaps(); + void rebuild_bitplanes(); + enum { + SLOT_color, + SLOT_depth, + SLOT_stencil, + SLOT_COUNT + }; + + GLuint _fbo; + int _rb_size_x; + int _rb_size_y; + GLuint _rb[SLOT_COUNT]; + PT(Texture) _tex[SLOT_COUNT]; + GLenum _attach_point[SLOT_COUNT]; + GLenum _slot_format[SLOT_COUNT]; + public: static TypeHandle get_class_type() { return _type_handle;