Browse Source

More work on glGraphicsBuffers (will compile soon)

Josh Yelon 20 years ago
parent
commit
f35a7469d0
2 changed files with 286 additions and 100 deletions
  1. 246 65
      panda/src/glstuff/glGraphicsBuffer_src.cxx
  2. 40 35
      panda/src/glstuff/glGraphicsBuffer_src.h

+ 246 - 65
panda/src/glstuff/glGraphicsBuffer_src.cxx

@@ -24,14 +24,32 @@ TypeHandle CLP(GraphicsBuffer)::_type_handle;
 //  Description:
 //  Description:
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 CLP(GraphicsBuffer)::
 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
   // Since the FBO never gets flipped, we get screenshots from the
   // same buffer we draw into.
   // same buffer we draw into.
   _screenshot_buffer_type = _draw_buffer_type;
   _screenshot_buffer_type = _draw_buffer_type;
+
+  // Initialize these.
+  _fbo = 0;
+  _rb_size_x = 0;
+  _rb_size_y = 0;
+  for (int i=0; i<SLOT_COUNT; i++) {
+    _rb[i] = 0;
+    _attached[i] = 0;
+  }
+  _attach_point[SLOT_depth]   = GL_DEPTH_ATTACHMENT_EXT;
+  _attach_point[SLOT_stencil] = GL_STENCIL_ATTACHMENT_EXT;
+  _attach_point[SLOT_color]   = GL_COLOR_ATTACHMENT0_EXT;
+
+  _slot_format[SLOT_depth]   = GL_DEPTH_COMPONENT;
+  _slot_format[SLOT_stencil] = GL_STENCIL_INDEX;
+  _slot_format[SLOT_color]   = GL_RGBA;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -44,7 +62,7 @@ CLP(GraphicsBuffer)::
 }
 }
  
  
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: wglGraphicsBuffer::begin_frame
+//     Function: glGraphicsBuffer::begin_frame
 //       Access: Public, Virtual
 //       Access: Public, Virtual
 //  Description: This function will be called within the draw thread
 //  Description: This function will be called within the draw thread
 //               before beginning rendering for a given frame.  It
 //               before beginning rendering for a given frame.  It
@@ -52,59 +70,232 @@ CLP(GraphicsBuffer)::
 //               if the frame should be rendered, or false if it
 //               if the frame should be rendered, or false if it
 //               should be skipped.
 //               should be skipped.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-bool wglGraphicsBuffer::
+bool glGraphicsBuffer::
 begin_frame(FrameMode mode) {
 begin_frame(FrameMode mode) {
   PStatTimer timer(_make_current_pcollector);
   PStatTimer timer(_make_current_pcollector);
-
-  begin_frame_spam();
-  if (_gsg == (GraphicsStateGuardian *)NULL) {
+  if (!_is_valid) {
     return false;
     return false;
   }
   }
 
 
   if (!_host->begin_frame(FM_parasite)) {
   if (!_host->begin_frame(FM_parasite)) {
     return false;
     return false;
   }
   }
+  
+  // Figure out the desired size of the buffer.
+  if (mode == FM_render) {
+    rebuild_bitplanes();
+    clear_cube_map_selection();
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     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 (_track_host_size) {
+  if (_creation_flags & GraphicsPipe::BF_track_host_size) {
     if ((_host->get_x_size() != _x_size)||
     if ((_host->get_x_size() != _x_size)||
         (_host->get_y_size() != _y_size)) {
         (_host->get_y_size() != _y_size)) {
       set_size_and_recalc(_host->get_x_size(),
       set_size_and_recalc(_host->get_x_size(),
                           _host->get_y_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; i<SLOT_COUNT; i++) {
+    attach[i] = 0;
+  }
+  for (int i=0; i<count_textures(); i++) {
+    if (get_rtm_mode(i) != RTM_bind_or_copy) {
+      continue;
+    }
+    Texture *tex = get_texture(i);
+    Texture::Format fmt = tex->get_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; slot<SLOT_COUNT; slot++) {
+    Texture *tex = attach[slot];
+    if (tex) {
+      // If the texture is already bound to the slot, and it's
+      // the right size, then no update of this slot is needed.
+      if ((_tex[slot] == tex)&&
+          (tex->get_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: wglGraphicsBuffer::end_frame
+//     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; slot<SLOT_COUNT; slot++) {
+    Texture *tex = _tex[slot];
+    if ((tex != 0) && (tex->uses_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: glGraphicsBuffer::end_frame
 //       Access: Public, Virtual
 //       Access: Public, Virtual
 //  Description: This function will be called within the draw thread
 //  Description: This function will be called within the draw thread
 //               after rendering is completed for a given frame.  It
 //               after rendering is completed for a given frame.  It
 //               should do whatever finalization is required.
 //               should do whatever finalization is required.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-void wglGraphicsBuffer::
+void glGraphicsBuffer::
 end_frame(FrameMode mode) {
 end_frame(FrameMode mode) {
   end_frame_spam();
   end_frame_spam();
   nassertv(_gsg != (GraphicsStateGuardian *)NULL);
   nassertv(_gsg != (GraphicsStateGuardian *)NULL);
 
 
   if (mode == FM_render) {
   if (mode == FM_render) {
-    end_render_texture();
     copy_to_textures();
     copy_to_textures();
   }
   }
 
 
   // Unbind the FBO
   // Unbind the FBO
+  glgsg->_glBindFramebuffer(GL_FRAMEBUFFER_EXT, 0);
   
   
+  if (mode == FM_render) {
+    generate_mipmaps();
+  }
+
   _host->end_frame(FM_parasite);
   _host->end_frame(FM_parasite);
 
 
   if (mode == FM_render) {
   if (mode == FM_render) {
@@ -127,68 +318,58 @@ end_frame(FrameMode mode) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void CLP(GraphicsBuffer)::
 void CLP(GraphicsBuffer)::
 select_cube_map(int cube_map_index) {
 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
 //       Access: Protected, Virtual
 //  Description: Opens the window right now.  Called from the window
 //  Description: Opens the window right now.  Called from the window
 //               thread.  Returns true if the window is successfully
 //               thread.  Returns true if the window is successfully
 //               opened, or false if there was a problem.
 //               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() {
 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);
-  }
 
 
-  // Scan the textures list to see which textures should be attached.
-  Texture *attach_color = 0;
-  Texture *attach_depth = 0;
-  Texture *attach_stencil = 0;
-  
+  // Check for support of relevant extensions.
+  glGraphicsStateGuardian *glgsg;
+  DCAST_INTO_R(glgsg, _gsg, false);
+  if ((!glgsg->_supports_framebuffer_object)||
+      (glgsg->_glDrawBuffers == 0)) {
+    return false;
+  }
 
 
-  // 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
 //       Access: Protected, Virtual
 //  Description: Closes the buffer right now.  Called from the window
 //  Description: Closes the buffer right now.  Called from the window
 //               thread.
 //               thread.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-void wglGraphicsBuffer::
+void glGraphicsBuffer::
 close_buffer() {
 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<SLOT_COUNT; i++) {
+    if (_rb[i] != 0) {
+      glgsg->_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);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////

+ 40 - 35
panda/src/glstuff/glGraphicsBuffer_src.h

@@ -24,38 +24,38 @@
 //       Class : glGraphicsBuffer
 //       Class : glGraphicsBuffer
 // Description : An offscreen render buffer.
 // 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 {
 class EXPCL_GL CLP(GraphicsBuffer) : public GraphicsBuffer {
 public:
 public:
-  CLP(GraphicsBuffer)(GraphicsPipe *pipe, GraphicsStateGuardian *gsg,
+  CLP(GraphicsBuffer)(GraphicsPipe *pipe,
                       const string &name,
                       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 ~CLP(GraphicsBuffer)();
 
 
   virtual bool begin_frame(FrameMode mode);
   virtual bool begin_frame(FrameMode mode);
@@ -68,20 +68,25 @@ protected:
   virtual bool open_buffer();
   virtual bool open_buffer();
 
 
 private:
 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:
 public:
   static TypeHandle get_class_type() {
   static TypeHandle get_class_type() {
     return _type_handle;
     return _type_handle;