Browse Source

FBO overhaul: support layered framebuffers (RTM_bind_layered), support render-to-2d-array, better respect FrameBufferProperties, more simple handling of RTP_depth_stencil

rdb 12 years ago
parent
commit
2274f2e651

+ 4 - 2
panda/src/cocoadisplay/cocoaGraphicsPipe.mm

@@ -314,7 +314,8 @@ make_output(const string &name,
         ((flags&BF_size_track_host)!=0)||
         ((flags&BF_size_track_host)!=0)||
         ((flags&BF_rtt_cumulative)!=0)||
         ((flags&BF_rtt_cumulative)!=0)||
         ((flags&BF_can_bind_color)!=0)||
         ((flags&BF_can_bind_color)!=0)||
-        ((flags&BF_can_bind_every)!=0)) {
+        ((flags&BF_can_bind_every)!=0)||
+        ((flags&BF_can_bind_layered)!=0)) {
       return NULL;
       return NULL;
     }
     }
     return new CocoaGraphicsWindow(engine, this, name, fb_prop, win_prop,
     return new CocoaGraphicsWindow(engine, this, name, fb_prop, win_prop,
@@ -359,7 +360,8 @@ make_output(const string &name,
     if (((flags&BF_require_parasite)!=0)||
     if (((flags&BF_require_parasite)!=0)||
         ((flags&BF_require_window)!=0)||
         ((flags&BF_require_window)!=0)||
         ((flags&BF_resizeable)!=0)||
         ((flags&BF_resizeable)!=0)||
-        ((flags&BF_size_track_host)!=0)) {
+        ((flags&BF_size_track_host)!=0)||
+        ((flags&BF_can_bind_layered)!=0)) {
       return NULL;
       return NULL;
     }
     }
 
 

+ 2 - 1
panda/src/display/graphicsEngine.cxx

@@ -368,7 +368,8 @@ make_output(GraphicsPipe *pipe,
       ((flags&GraphicsPipe::BF_refuse_parasite)==0)&&
       ((flags&GraphicsPipe::BF_refuse_parasite)==0)&&
       ((flags&GraphicsPipe::BF_can_bind_color)==0)&&
       ((flags&GraphicsPipe::BF_can_bind_color)==0)&&
       ((flags&GraphicsPipe::BF_can_bind_every)==0)&&
       ((flags&GraphicsPipe::BF_can_bind_every)==0)&&
-      ((flags&GraphicsPipe::BF_rtt_cumulative)==0)) {
+      ((flags&GraphicsPipe::BF_rtt_cumulative)==0)&&
+      ((flags&GraphicsPipe::BF_can_bind_layered)==0)) {
     if ((flags&GraphicsPipe::BF_fb_props_optional) ||
     if ((flags&GraphicsPipe::BF_fb_props_optional) ||
         (host->get_fb_properties().subsumes(fb_prop))) {
         (host->get_fb_properties().subsumes(fb_prop))) {
       can_use_parasite = true;
       can_use_parasite = true;

+ 32 - 13
panda/src/display/graphicsOutput.cxx

@@ -322,9 +322,9 @@ add_render_texture(Texture *tex, RenderTextureMode mode,
 
 
   // Choose a default bitplane.
   // Choose a default bitplane.
   if (plane == RTP_COUNT) {
   if (plane == RTP_COUNT) {
-    if (tex->get_format()==Texture::F_depth_stencil) {
+    if (tex->get_format() == Texture::F_depth_stencil) {
       plane = RTP_depth_stencil;
       plane = RTP_depth_stencil;
-    } else if (tex->get_format()==Texture::F_depth_component) {
+    } else if (tex->get_format() == Texture::F_depth_component) {
       plane = RTP_depth;
       plane = RTP_depth;
     } else {
     } else {
       plane = RTP_color;
       plane = RTP_color;
@@ -339,6 +339,7 @@ add_render_texture(Texture *tex, RenderTextureMode mode,
     tex->set_match_framebuffer_format(true);
     tex->set_match_framebuffer_format(true);
   } else if (plane == RTP_depth_stencil) {
   } else if (plane == RTP_depth_stencil) {
     tex->set_format(Texture::F_depth_stencil);
     tex->set_format(Texture::F_depth_stencil);
+    tex->set_component_type(Texture::T_unsigned_int_24_8);
     tex->set_match_framebuffer_format(true);
     tex->set_match_framebuffer_format(true);
   } else if ((plane == RTP_color)||
   } else if ((plane == RTP_color)||
              (plane == RTP_aux_rgba_0)||
              (plane == RTP_aux_rgba_0)||
@@ -358,6 +359,7 @@ add_render_texture(Texture *tex, RenderTextureMode mode,
               (plane == RTP_aux_float_2)||
               (plane == RTP_aux_float_2)||
               (plane == RTP_aux_float_3)) {
               (plane == RTP_aux_float_3)) {
     tex->set_format(Texture::F_rgba32);
     tex->set_format(Texture::F_rgba32);
+    tex->set_component_type(Texture::T_float);
     tex->set_match_framebuffer_format(true);
     tex->set_match_framebuffer_format(true);
   } else {
   } else {
     display_cat.error() <<
     display_cat.error() <<
@@ -368,17 +370,32 @@ add_render_texture(Texture *tex, RenderTextureMode mode,
   // Go ahead and tell the texture our anticipated size, even if it
   // Go ahead and tell the texture our anticipated size, even if it
   // might be inaccurate (particularly if this is a GraphicsWindow,
   // might be inaccurate (particularly if this is a GraphicsWindow,
   // which has system-imposed restrictions on size).
   // which has system-imposed restrictions on size).
-  tex->set_size_padded(get_x_size(), get_y_size());
+  tex->set_size_padded(get_x_size(), get_y_size(), tex->get_z_size());
 
 
-  if (mode == RTM_bind_or_copy) {
-    if (!support_render_texture || !get_supports_render_texture()) {
-      // Binding is not supported or it is disabled, so just fall back
-      // to copy instead.
+  if (!support_render_texture || !get_supports_render_texture()) {
+    // Binding is not supported or it is disabled, so just fall back
+    // to copy instead.
+    if (mode == RTM_bind_or_copy) {
       mode = RTM_copy_texture;
       mode = RTM_copy_texture;
+    } else if (mode == RTM_bind_layered) {
+      // We can't fallback to copy, because that doesn't work
+      // for layered textures.  The best thing we can do is raise
+      // an error message.
+      display_cat.error() <<
+        "add_render_texture: RTM_bind_layered was requested but "
+        "render-to-texture is not supported or has been disabled!\n";
     }
     }
   }
   }
 
 
-  if (mode == RTM_bind_or_copy) {
+  if (mode == RTM_bind_layered && _gsg != NULL && !_gsg->get_supports_geometry_shaders()) {
+    // Layered FBOs require a geometry shader to write to
+    // any but the first layer.
+    display_cat.warning() <<
+      "add_render_texture: RTM_bind_layered was requested but "
+      "geometry shaders are not supported!\n";
+  }
+
+  if (mode == RTM_bind_or_copy || mode == RTM_bind_layered) {
     // If we're still planning on binding, indicate it in texture
     // If we're still planning on binding, indicate it in texture
     // properly.
     // properly.
     tex->set_render_to_texture(true);
     tex->set_render_to_texture(true);
@@ -1037,8 +1054,7 @@ make_cube_map(const string &name, int size, NodePath &camera_rig,
   buffer->set_clear_depth_active(false);
   buffer->set_clear_depth_active(false);
   buffer->set_clear_stencil_active(false);
   buffer->set_clear_stencil_active(false);
 
 
-  PT(Lens) lens = new PerspectiveLens;
-  lens->set_fov(90.0f);
+  PT(Lens) lens = new PerspectiveLens(90, 90);
 
 
   for (int i = 0; i < 6; i++) {
   for (int i = 0; i < 6; i++) {
     PT(Camera) camera = new Camera(cube_faces[i]._name);
     PT(Camera) camera = new Camera(cube_faces[i]._name);
@@ -1336,7 +1352,7 @@ change_scenes(DisplayRegionPipelineReader *new_dr) {
       RenderTextureMode rtm_mode = (*ri)._rtm_mode;
       RenderTextureMode rtm_mode = (*ri)._rtm_mode;
       Texture *texture = (*ri)._texture;
       Texture *texture = (*ri)._texture;
       if (rtm_mode != RTM_none) {
       if (rtm_mode != RTM_none) {
-        if (rtm_mode == RTM_bind_or_copy) {
+        if (rtm_mode == RTM_bind_or_copy || rtm_mode == RTM_bind_layered) {
           // In render-to-texture mode, switch the rendering backend to
           // In render-to-texture mode, switch the rendering backend to
           // the new cube map face, so that the subsequent frame will be
           // the new cube map face, so that the subsequent frame will be
           // rendered to the new face.
           // rendered to the new face.
@@ -1469,7 +1485,7 @@ prepare_for_deletion() {
   // buffer until all the textures are gone too.
   // buffer until all the textures are gone too.
   RenderTextures::iterator ri;
   RenderTextures::iterator ri;
   for (ri = cdata->_textures.begin(); ri != cdata->_textures.end(); ++ri) {
   for (ri = cdata->_textures.begin(); ri != cdata->_textures.end(); ++ri) {
-    if ((*ri)._rtm_mode == RTM_bind_or_copy) {
+    if ((*ri)._rtm_mode == RTM_bind_or_copy || (*ri)._rtm_mode == RTM_bind_layered) {
       _hold_textures.push_back((*ri)._texture);
       _hold_textures.push_back((*ri)._texture);
     }
     }
   }
   }
@@ -1488,7 +1504,10 @@ prepare_for_deletion() {
 //     Function: GraphicsOutput::promote_to_copy_texture
 //     Function: GraphicsOutput::promote_to_copy_texture
 //       Access: Protected
 //       Access: Protected
 //  Description: If any textures are marked RTM_bind_or_copy, change
 //  Description: If any textures are marked RTM_bind_or_copy, change
-//               them to RTM_copy_texture.
+//               them to RTM_copy_texture.  This does not change
+//               textures that are set to RTM_bind_layered, as
+//               layered framebuffers aren't supported with
+//               RTM_copy_texture.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void GraphicsOutput::
 void GraphicsOutput::
 promote_to_copy_texture() {
 promote_to_copy_texture() {

+ 21 - 3
panda/src/display/graphicsOutput.h

@@ -83,11 +83,29 @@ private:
 PUBLISHED:
 PUBLISHED:
   enum RenderTextureMode {
   enum RenderTextureMode {
     RTM_none,
     RTM_none,
+
+    // Try to render to the texture directly, but if that is
+    // not possible, fall back to RTM_copy_texture.
     RTM_bind_or_copy,
     RTM_bind_or_copy,
+
+    // Copy the image from the buffer to the texture every frame.
     RTM_copy_texture,
     RTM_copy_texture,
+
+    // Copy the image from the buffer to system RAM every frame.
     RTM_copy_ram,
     RTM_copy_ram,
+
+    // Copy the image from the buffer to the texture after a
+    // call to trigger_copy().
     RTM_triggered_copy_texture,
     RTM_triggered_copy_texture,
+
+    // Copy the image from the buffer to system RAM after a
+    // call to trigger_copy().
     RTM_triggered_copy_ram,
     RTM_triggered_copy_ram,
+
+    // Render directly to a layered texture, such as a cube map,
+    // 3D texture or 2D texture array.  The layer that is being
+    // rendered to is selected by a geometry shader.
+    RTM_bind_layered,
   };
   };
 
 
   // There are many reasons to call begin_frame/end_frame.
   // There are many reasons to call begin_frame/end_frame.
@@ -167,7 +185,7 @@ PUBLISHED:
   INLINE int get_child_sort() const;
   INLINE int get_child_sort() const;
 
 
   INLINE void trigger_copy();
   INLINE void trigger_copy();
-  
+
   INLINE DisplayRegion *make_display_region();
   INLINE DisplayRegion *make_display_region();
   INLINE DisplayRegion *make_display_region(PN_stdfloat l, PN_stdfloat r, PN_stdfloat b, PN_stdfloat t);
   INLINE DisplayRegion *make_display_region(PN_stdfloat l, PN_stdfloat r, PN_stdfloat b, PN_stdfloat t);
   DisplayRegion *make_display_region(const LVecBase4 &dimensions);
   DisplayRegion *make_display_region(const LVecBase4 &dimensions);
@@ -233,7 +251,7 @@ public:
   virtual void clear_pipe();
   virtual void clear_pipe();
 
 
   void set_size_and_recalc(int x, int y);
   void set_size_and_recalc(int x, int y);
-  
+
   // It is an error to call any of the following methods from any
   // It is an error to call any of the following methods from any
   // thread other than the draw thread.  These methods are normally
   // thread other than the draw thread.  These methods are normally
   // called by the GraphicsEngine.
   // called by the GraphicsEngine.
@@ -262,7 +280,7 @@ protected:
   void prepare_for_deletion();
   void prepare_for_deletion();
   void promote_to_copy_texture();
   void promote_to_copy_texture();
   bool copy_to_textures();
   bool copy_to_textures();
-  
+
   INLINE void begin_frame_spam(FrameMode mode);
   INLINE void begin_frame_spam(FrameMode mode);
   INLINE void end_frame_spam(FrameMode mode);
   INLINE void end_frame_spam(FrameMode mode);
   INLINE void clear_cube_map_selection();
   INLINE void clear_cube_map_selection();

+ 1 - 0
panda/src/display/graphicsPipe.h

@@ -89,6 +89,7 @@ PUBLISHED:
     BF_fb_props_optional   = 0x0800, // FrameBufferProperties can be ignored.
     BF_fb_props_optional   = 0x0800, // FrameBufferProperties can be ignored.
     BF_size_square         = 0x1000, // x_size must equal y_size (e.g. for cube maps)
     BF_size_square         = 0x1000, // x_size must equal y_size (e.g. for cube maps)
     BF_size_power_2        = 0x2000, // x_size and y_size must each be a power of two
     BF_size_power_2        = 0x2000, // x_size and y_size must each be a power of two
+    BF_can_bind_layered    = 0x4000, // Need capability: support RTM_bind_layered.
   };
   };
 
 
   INLINE bool is_valid() const;
   INLINE bool is_valid() const;

+ 2 - 2
panda/src/display/graphicsStateGuardian.cxx

@@ -2722,9 +2722,9 @@ make_shadow_buffer(const NodePath &light_np, GraphicsOutputBase *host) {
       display_cat.error()
       display_cat.error()
         << "PointLight shadow buffers must have an equal width and height!\n";
         << "PointLight shadow buffers must have an equal width and height!\n";
     }
     }
-    tex->setup_cube_map(light->_sb_xsize, Texture::T_float, Texture::F_depth_component);
+    tex->setup_cube_map(light->_sb_xsize, Texture::T_unsigned_byte, Texture::F_depth_component);
   } else {
   } else {
-    tex->setup_2d_texture(light->_sb_xsize, light->_sb_ysize, Texture::T_float, Texture::F_depth_component);
+    tex->setup_2d_texture(light->_sb_xsize, light->_sb_ysize, Texture::T_unsigned_byte, Texture::F_depth_component);
   }
   }
   tex->make_ram_image();
   tex->make_ram_image();
   sbuffer->add_render_texture(tex, GraphicsOutput::RTM_bind_or_copy, GraphicsOutput::RTP_depth);
   sbuffer->add_render_texture(tex, GraphicsOutput::RTM_bind_or_copy, GraphicsOutput::RTP_depth);

+ 424 - 221
panda/src/glstuff/glGraphicsBuffer_src.cxx

@@ -13,7 +13,6 @@
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 
 
 TypeHandle CLP(GraphicsBuffer)::_type_handle;
 TypeHandle CLP(GraphicsBuffer)::_type_handle;
-  
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: glGraphicsBuffer::Constructor
 //     Function: glGraphicsBuffer::Constructor
@@ -37,8 +36,6 @@ CLP(GraphicsBuffer)(GraphicsEngine *engine, GraphicsPipe *pipe,
   _screenshot_buffer_type = RenderBuffer::T_front;
   _screenshot_buffer_type = RenderBuffer::T_front;
 
 
   // Initialize these.
   // Initialize these.
-  _num_faces = 0;
-  memset(_fbo, 0, sizeof(_fbo));
   _fbo_multisample = 0;
   _fbo_multisample = 0;
   _initial_clear = true;
   _initial_clear = true;
   _needs_rebuild = true;
   _needs_rebuild = true;
@@ -75,6 +72,7 @@ CLP(GraphicsBuffer)(GraphicsEngine *engine, GraphicsPipe *pipe,
 
 
   _rb_size_x = 0;
   _rb_size_x = 0;
   _rb_size_y = 0;
   _rb_size_y = 0;
+  _rb_size_z = 0;
   for (int i=0; i<RTP_COUNT; i++) {
   for (int i=0; i<RTP_COUNT; i++) {
     _rb[i] = 0;
     _rb[i] = 0;
     _tex[i] = 0;
     _tex[i] = 0;
@@ -100,14 +98,14 @@ CLP(GraphicsBuffer)::
     CLP(GraphicsBuffer) *graphics_buffer;
     CLP(GraphicsBuffer) *graphics_buffer;
     list <CLP(GraphicsBuffer) *>::iterator graphics_buffer_iterator;
     list <CLP(GraphicsBuffer) *>::iterator graphics_buffer_iterator;
 
 
-    graphics_buffer_iterator = _shared_depth_buffer_list.begin( );
-    while (graphics_buffer_iterator != _shared_depth_buffer_list.end( )) {
+    graphics_buffer_iterator = _shared_depth_buffer_list.begin();
+    while (graphics_buffer_iterator != _shared_depth_buffer_list.end()) {
       graphics_buffer = (*graphics_buffer_iterator);
       graphics_buffer = (*graphics_buffer_iterator);
       if (graphics_buffer) {
       if (graphics_buffer) {
         // this call removes the entry from the list
         // this call removes the entry from the list
-        graphics_buffer -> unshare_depth_buffer();
+        graphics_buffer->unshare_depth_buffer();
       }
       }
-      graphics_buffer_iterator = _shared_depth_buffer_list.begin( );
+      graphics_buffer_iterator = _shared_depth_buffer_list.begin();
     }
     }
   }
   }
 }
 }
@@ -164,9 +162,9 @@ begin_frame(FrameMode mode, Thread *current_thread) {
         _needs_rebuild = true;
         _needs_rebuild = true;
       }
       }
     }
     }
-        
+
     rebuild_bitplanes();
     rebuild_bitplanes();
-      
+
     if (_needs_rebuild) {
     if (_needs_rebuild) {
       // If we still need rebuild, something went wrong with
       // If we still need rebuild, something went wrong with
       // rebuild_bitplanes().
       // rebuild_bitplanes().
@@ -196,24 +194,28 @@ check_fbo() {
     GLCAT.error() << "EXT_framebuffer_object reports non-framebuffer-completeness:\n";
     GLCAT.error() << "EXT_framebuffer_object reports non-framebuffer-completeness:\n";
     switch(status) {
     switch(status) {
     case GL_FRAMEBUFFER_UNSUPPORTED_EXT:
     case GL_FRAMEBUFFER_UNSUPPORTED_EXT:
-      GLCAT.error() << "FRAMEBUFFER_UNSUPPORTED_EXT"; break;
+      GLCAT.error() << "FRAMEBUFFER_UNSUPPORTED"; break;
     case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT:
     case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT:
-      GLCAT.error() << "FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT"; break;
+      GLCAT.error() << "FRAMEBUFFER_INCOMPLETE_ATTACHMENT"; break;
     case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT:
     case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT:
-      GLCAT.error() << "FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT"; break;
+      GLCAT.error() << "FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT"; break;
     case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT:
     case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT:
-      GLCAT.error() << "FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT"; break;
+      GLCAT.error() << "FRAMEBUFFER_INCOMPLETE_DIMENSIONS"; break;
 #ifndef OPENGLES_2
 #ifndef OPENGLES_2
     case GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT:
     case GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT:
-      GLCAT.error() << "FRAMEBUFFER_INCOMPLETE_FORMATS_EXT"; break;
+      GLCAT.error() << "FRAMEBUFFER_INCOMPLETE_FORMATS"; break;
 #endif
 #endif
 #ifndef OPENGLES
 #ifndef OPENGLES
     case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT:
     case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT:
-      GLCAT.error() << "FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT"; break;
+      GLCAT.error() << "FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER"; break;
     case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT:
     case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT:
-      GLCAT.error() << "FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT"; break;
+      GLCAT.error() << "FRAMEBUFFER_INCOMPLETE_READ_BUFFER"; break;
     case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_EXT:
     case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_EXT:
-      GLCAT.error() << "FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_EXT"; break;
+      GLCAT.error() << "FRAMEBUFFER_INCOMPLETE_MULTISAMPLE"; break;
+    case GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS_ARB:
+      GLCAT.error() << "FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS"; break;
+    case GL_FRAMEBUFFER_INCOMPLETE_LAYER_COUNT_ARB:
+      GLCAT.error() << "FRAMEBUFFER_INCOMPLETE_LAYER_COUNT"; break;
 #endif
 #endif
     default:
     default:
       GLCAT.error() << "UNKNOWN PROBLEM " << (int)status; break;
       GLCAT.error() << "UNKNOWN PROBLEM " << (int)status; break;
@@ -246,10 +248,14 @@ rebuild_bitplanes() {
   DCAST_INTO_V(glgsg, _gsg);
   DCAST_INTO_V(glgsg, _gsg);
 
 
   if (!_needs_rebuild) {
   if (!_needs_rebuild) {
-    glgsg->bind_fbo(_fbo[0]);
+    if (_fbo.size() > 0) {
+      glgsg->bind_fbo(_fbo[0]);
+    } else {
+      glgsg->bind_fbo(0);
+    }
     return;
     return;
   }
   }
-  
+
   // Calculate bitplane size.  This can be larger than the buffer.
   // Calculate bitplane size.  This can be larger than the buffer.
   if (_creation_flags & GraphicsPipe::BF_size_track_host) {
   if (_creation_flags & GraphicsPipe::BF_size_track_host) {
     if ((_host->get_x_size() != _x_size)||
     if ((_host->get_x_size() != _x_size)||
@@ -271,34 +277,69 @@ rebuild_bitplanes() {
     _rb_size_y = bitplane_y;
     _rb_size_y = bitplane_y;
     rb_resize = true;
     rb_resize = true;
   }
   }
-  
+  _rb_size_z = 1;
+
+  int num_fbos = 1;
+
   // These variables indicate what should be bound to each bitplane.
   // These variables indicate what should be bound to each bitplane.
   Texture *attach[RTP_COUNT];
   Texture *attach[RTP_COUNT];
   memset(attach, 0, sizeof(Texture *) * RTP_COUNT);
   memset(attach, 0, sizeof(Texture *) * RTP_COUNT);
-  
+
   // Sort the textures list into appropriate slots.
   // Sort the textures list into appropriate slots.
-  bool any_cube_maps = false;
   {
   {
     CDReader cdata(_cycler);
     CDReader cdata(_cycler);
+
+    // Determine whether this will be a layered or a regular FBO.
+    // If layered, the number of _rb_size_z will be higher than 1.
     for (size_t i = 0; i != cdata->_textures.size(); ++i) {
     for (size_t i = 0; i != cdata->_textures.size(); ++i) {
       const RenderTexture &rt = cdata->_textures[i];
       const RenderTexture &rt = cdata->_textures[i];
       RenderTextureMode rtm_mode = rt._rtm_mode;
       RenderTextureMode rtm_mode = rt._rtm_mode;
-      if (rtm_mode != RTM_bind_or_copy) {
-        continue;
+      Texture *tex = rt._texture;
+
+      if (rtm_mode == RTM_bind_layered) {
+        _rb_size_z = max(_rb_size_z, tex->get_z_size());
       }
       }
+    }
+
+    for (size_t i = 0; i != cdata->_textures.size(); ++i) {
+      const RenderTexture &rt = cdata->_textures[i];
+      RenderTextureMode rtm_mode = rt._rtm_mode;
       Texture *tex = rt._texture;
       Texture *tex = rt._texture;
-      RenderTexturePlane plane = rt._plane;
-      
-      // If it's a not a 2D texture or a cube map, punt it.
+
+      if (rtm_mode == RTM_bind_layered) {
+        if (tex->get_z_size() != _rb_size_z) {
+          GLCAT.warning()
+           << "All textures attached to layered FBO should have the same layer count!\n";
+        }
+      } else if (tex->get_z_size() > 1) {
+        num_fbos = max(num_fbos, tex->get_z_size());
+      }
+
+      if (rtm_mode != RTM_bind_or_copy && rtm_mode != RTM_bind_layered) {
+        continue;
+      }
+
+      // If we can't bind this type of texture, punt it.
       if ((tex->get_texture_type() != Texture::TT_2d_texture) &&
       if ((tex->get_texture_type() != Texture::TT_2d_texture) &&
+          (tex->get_texture_type() != Texture::TT_3d_texture) &&
+          (tex->get_texture_type() != Texture::TT_2d_texture_array) &&
           (tex->get_texture_type() != Texture::TT_cube_map)) {
           (tex->get_texture_type() != Texture::TT_cube_map)) {
         ((CData *)cdata.p())->_textures[i]._rtm_mode = RTM_copy_texture;
         ((CData *)cdata.p())->_textures[i]._rtm_mode = RTM_copy_texture;
         continue;
         continue;
       }
       }
 
 
+      if (_rb_size_z > 1 && tex->get_texture_type() == Texture::TT_2d_texture) {
+        // We can't bind a 2D texture to a layered FBO.  If the user happened
+        // to request RTM_bind_layered for a 2D texture, that's just silly,
+        // and we can't render to anything but the first layer anyway.
+        ((CData *)cdata.p())->_textures[i]._rtm_mode = RTM_copy_texture;
+        continue;
+      }
+
       // If I can't find an appropriate slot, or if there's
       // If I can't find an appropriate slot, or if there's
       // already a texture bound to this slot, then punt
       // already a texture bound to this slot, then punt
       // this texture.
       // this texture.
+      RenderTexturePlane plane = rt._plane;
       if (attach[plane]) {
       if (attach[plane]) {
         ((CData *)cdata.p())->_textures[i]._rtm_mode = RTM_copy_texture;
         ((CData *)cdata.p())->_textures[i]._rtm_mode = RTM_copy_texture;
         continue;
         continue;
@@ -306,55 +347,93 @@ rebuild_bitplanes() {
 
 
       // Assign the texture to this slot.
       // Assign the texture to this slot.
       attach[plane] = tex;
       attach[plane] = tex;
-      if (tex->get_texture_type() == Texture::TT_cube_map) {
-        any_cube_maps = true;
-      }
     }
     }
   }
   }
-  
+
+  // Decide whether we should use a depth stencil or just a regular depth attachment.
+  // If nothing was attached to either RTP_depth_stencil or RTP_depth, we use a
+  // depth-stencil renderbuffer.
+  _use_depth_stencil = false;
+  if (_gsg->get_supports_depth_stencil()) {
+    if (attach[RTP_depth_stencil]) {
+      // This is the obvious case, of course.
+      _use_depth_stencil = true;
+
+    } else if (attach[RTP_depth]) {
+      // We won't use a depth stencil texture as the user
+      // explicitly bound something to RTP_depth.
+      _use_depth_stencil = false;
+
+    } else if (_fb_properties.get_depth_bits() > 24) {
+      // We can't give more than 24 depth bits with a depth-stencil buffer.
+      _use_depth_stencil = false;
+
+    } else if (_fb_properties.get_stencil_bits() > 0) {
+      // The user requested stencil bits.  Let's take the hint.
+      _use_depth_stencil = true;
+
+    } else if (_fb_properties.get_depth_bits() > 0) {
+      // Let's use a depth stencil buffer by default, if a depth
+      // buffer was requested.
+      _use_depth_stencil = true;
+    }
+  }
+
+  // Knowing this, we can already be a tiny bit
+  // more accurate about the framebuffer properties.
+  if (_use_depth_stencil) {
+    _fb_properties.set_depth_bits(24);
+    _fb_properties.set_stencil_bits(8);
+  } else {
+    _fb_properties.set_stencil_bits(0);
+  }
+
   // Having both a depth texture and a depth_stencil texture is
   // Having both a depth texture and a depth_stencil texture is
   // invalid: depth_stencil implies depth, and we can't bind them
   // invalid: depth_stencil implies depth, and we can't bind them
   // both.  Detect that case, normalize it, and complain.
   // both.  Detect that case, normalize it, and complain.
-  if (( attach[RTP_depth] != NULL ) && ( attach[RTP_depth_stencil] != NULL )) {
+  if (_use_depth_stencil && attach[RTP_depth] && attach[RTP_depth_stencil]) {
     attach[RTP_depth] = NULL;
     attach[RTP_depth] = NULL;
-    GLCAT.warning() << "Attempt to bind both Depth and DepthStencil bitplanes.\n";
-  }
-
-  _num_faces = 1;
-  if (any_cube_maps) {
-    _num_faces = 6;
+    GLCAT.warning() << "Attempt to bind both RTP_depth and RTP_depth_stencil bitplanes.\n";
   }
   }
 
 
   // Now create the FBO's.
   // Now create the FBO's.
-  for (int face = 0; face < _num_faces; ++face) {
+  _fbo.reserve(num_fbos);
+  for (int layer = 0; layer < num_fbos; ++layer) {
+    if (layer >= _fbo.size()) {
+      _fbo.push_back(0);
+    }
+
     // Bind the FBO
     // Bind the FBO
-    if (_fbo[face] == 0) {
-      glgsg->_glGenFramebuffers(1, &_fbo[face]);
-      if (_fbo[face] == 0) {
+    if (_fbo[layer] == 0) {
+      glgsg->_glGenFramebuffers(1, &_fbo[layer]);
+      if (_fbo[layer] == 0) {
         report_my_gl_errors();
         report_my_gl_errors();
         return;
         return;
       }
       }
     }
     }
-    glgsg->bind_fbo(_fbo[face]);
+    glgsg->bind_fbo(_fbo[layer]);
 
 
     // For all slots, update the slot.
     // For all slots, update the slot.
-    
-    bind_slot(face, rb_resize, attach, RTP_depth_stencil, GL_DEPTH_ATTACHMENT_EXT);
-    bind_slot(face, rb_resize, attach, RTP_depth, GL_DEPTH_ATTACHMENT_EXT);
-    bind_slot(face, rb_resize, attach, RTP_color, GL_COLOR_ATTACHMENT0_EXT);
+    if (_use_depth_stencil) {
+      bind_slot(layer, rb_resize, attach, RTP_depth_stencil, GL_DEPTH_ATTACHMENT_EXT);
+    } else if (attach[RTP_depth] || _fb_properties.get_depth_bits() > 0) {
+      bind_slot(layer, rb_resize, attach, RTP_depth, GL_DEPTH_ATTACHMENT_EXT);
+    }
+
+    int next = GL_COLOR_ATTACHMENT0_EXT;
+    if (attach[RTP_color] || _fb_properties.get_color_bits() > 0) {
+      bind_slot(layer, rb_resize, attach, RTP_color, next++);
+    }
+
 #ifndef OPENGLES
 #ifndef OPENGLES
-    int next = GL_COLOR_ATTACHMENT1_EXT;
     for (int i=0; i<_fb_properties.get_aux_rgba(); i++) {
     for (int i=0; i<_fb_properties.get_aux_rgba(); i++) {
-      bind_slot(face, rb_resize, attach, (RenderTexturePlane)(RTP_aux_rgba_0+i), next);
-      next += 1;
+      bind_slot(layer, rb_resize, attach, (RenderTexturePlane)(RTP_aux_rgba_0+i), next++);
     }
     }
     for (int i=0; i<_fb_properties.get_aux_hrgba(); i++) {
     for (int i=0; i<_fb_properties.get_aux_hrgba(); i++) {
-      bind_slot(face, rb_resize, attach, (RenderTexturePlane)(RTP_aux_hrgba_0+i), next);
-      next += 1;
+      bind_slot(layer, rb_resize, attach, (RenderTexturePlane)(RTP_aux_hrgba_0+i), next++);
     }
     }
     for (int i=0; i<_fb_properties.get_aux_float(); i++) {
     for (int i=0; i<_fb_properties.get_aux_float(); i++) {
-      bind_slot(face, rb_resize, attach, (RenderTexturePlane)(RTP_aux_float_0+i), next);
-      next += 1;
+      bind_slot(layer, rb_resize, attach, (RenderTexturePlane)(RTP_aux_float_0+i), next++);
     }
     }
 #endif  // OPENGLES
 #endif  // OPENGLES
 
 
@@ -366,26 +445,32 @@ rebuild_bitplanes() {
   }
   }
 
 
 #ifndef OPENGLES
 #ifndef OPENGLES
-  // Setup any required multisample buffers.
-  if (_requested_multisamples) {
+  // Setup any required multisample buffers.  Does not work for layered buffers.
+  if (_requested_multisamples && _rb_size_z == 1) {
     if (_fbo_multisample == 0) {
     if (_fbo_multisample == 0) {
       glgsg->_glGenFramebuffers(1, &_fbo_multisample);
       glgsg->_glGenFramebuffers(1, &_fbo_multisample);
     }
     }
     glgsg->bind_fbo(_fbo_multisample);
     glgsg->bind_fbo(_fbo_multisample);
-    bind_slot_multisample(rb_resize, attach, RTP_depth, GL_DEPTH_ATTACHMENT_EXT);
-    bind_slot_multisample(rb_resize, attach, RTP_color, GL_COLOR_ATTACHMENT0_EXT);
 
 
-    int next = GL_COLOR_ATTACHMENT1_EXT;
+    if (_use_depth_stencil || attach[RTP_depth] || _fb_properties.get_depth_bits() > 0) {
+      bind_slot_multisample(rb_resize, attach, RTP_depth, GL_DEPTH_ATTACHMENT_EXT);
+    }
+
+    int next = GL_COLOR_ATTACHMENT0_EXT;
+    if (attach[RTP_color] || _fb_properties.get_color_bits() > 0) {
+      bind_slot_multisample(rb_resize, attach, RTP_color, next++);
+    }
+
     for (int i=0; i<_fb_properties.get_aux_rgba(); i++) {
     for (int i=0; i<_fb_properties.get_aux_rgba(); i++) {
-      bind_slot_multisample(rb_resize, attach, (RenderTexturePlane)(RTP_aux_rgba_0+i), next);
+      bind_slot_multisample(rb_resize, attach, (RenderTexturePlane)(RTP_aux_rgba_0+i), next++);
       next += 1;
       next += 1;
     }
     }
     for (int i=0; i<_fb_properties.get_aux_hrgba(); i++) {
     for (int i=0; i<_fb_properties.get_aux_hrgba(); i++) {
-      bind_slot_multisample(rb_resize, attach, (RenderTexturePlane)(RTP_aux_hrgba_0+i), next);
+      bind_slot_multisample(rb_resize, attach, (RenderTexturePlane)(RTP_aux_hrgba_0+i), next++);
       next += 1;
       next += 1;
     }
     }
     for (int i=0; i<_fb_properties.get_aux_float(); i++) {
     for (int i=0; i<_fb_properties.get_aux_float(); i++) {
-      bind_slot_multisample(rb_resize, attach, (RenderTexturePlane)(RTP_aux_float_0+i), next);
+      bind_slot_multisample(rb_resize, attach, (RenderTexturePlane)(RTP_aux_float_0+i), next++);
       next += 1;
       next += 1;
     }
     }
     glEnable(GL_MULTISAMPLE);
     glEnable(GL_MULTISAMPLE);
@@ -432,96 +517,155 @@ rebuild_bitplanes() {
 //               specified bitplane.
 //               specified bitplane.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void CLP(GraphicsBuffer)::
 void CLP(GraphicsBuffer)::
-bind_slot(int face, bool rb_resize, Texture **attach, RenderTexturePlane slot, GLenum attachpoint) {
+bind_slot(int layer, bool rb_resize, Texture **attach, RenderTexturePlane slot, GLenum attachpoint) {
   CLP(GraphicsStateGuardian) *glgsg;
   CLP(GraphicsStateGuardian) *glgsg;
   DCAST_INTO_V(glgsg, _gsg);
   DCAST_INTO_V(glgsg, _gsg);
 
 
-#ifdef OPENGLES
-  GLuint glFormat = GL_RGBA4;
-#else
-  GLuint glFormat = GL_RGBA;
-  switch (slot) {
-    case RTP_aux_rgba_0:
-    case RTP_aux_rgba_1:
-    case RTP_aux_rgba_2:
-    case RTP_aux_rgba_3:
-      glFormat = GL_RGBA;
-      break;
-    case RTP_aux_hrgba_0:
-    case RTP_aux_hrgba_1:
-    case RTP_aux_hrgba_2:
-    case RTP_aux_hrgba_3:
-      glFormat = GL_RGBA16F_ARB;
-      break;
-  };
-#endif
-
   Texture *tex = attach[slot];
   Texture *tex = attach[slot];
-  if (tex) {
-    bool is_cube_map = (tex->get_texture_type() == Texture::TT_cube_map);
+  _tex[slot] = tex;
+
+  if (tex && layer >= tex->get_z_size()) {
+    // If the requested layer index exceeds the number of layers
+    // in the texture, we will not bind this layer.
+    tex = NULL;
+  }
+
+  if (!tex && _rb_size_z > 1) {
+    // Since there is no such thing as a layered renderbuffer (to my knowledge),
+    // we have to create a dummy texture to render to if no texture was provided.
+    tex = new Texture();
 
 
-    GLenum target = GL_TEXTURE_2D;
-    if (is_cube_map) {
-      target = GL_TEXTURE_CUBE_MAP_POSITIVE_X + face;
+    if (_rb_size_z > 1) {
+      // Apparently, it doesn't make a difference whether we use setup_cube_map
+      // or setup_2d_texture_array here, since it's the same internal storage.
+      tex->setup_2d_texture_array(_rb_size_z);
+    } else {
+      tex->setup_2d_texture();
+    }
+  }
+
+  if (tex) {
+    GLenum target = glgsg->get_texture_target(tex->get_texture_type());
+    if (target == GL_TEXTURE_CUBE_MAP) {
+      target = GL_TEXTURE_CUBE_MAP_POSITIVE_X + layer;
     }
     }
 
 
     // Bind the texture to the slot.
     // Bind the texture to the slot.
     tex->set_x_size(_rb_size_x);
     tex->set_x_size(_rb_size_x);
     tex->set_y_size(_rb_size_y);
     tex->set_y_size(_rb_size_y);
+    if (target != GL_TEXTURE_CUBE_MAP && _rb_size_z > 1) {
+      tex->set_z_size(_rb_size_z);
+    }
     tex->set_pad_size(_rb_size_x - _x_size, _rb_size_y - _y_size);
     tex->set_pad_size(_rb_size_x - _x_size, _rb_size_y - _y_size);
 
 
+    // Adjust the texture format based on the requested framebuffer settings.
+    switch (slot) {
+      case RTP_depth:
+        if (_fb_properties.get_depth_bits() >= 32) {
+          tex->set_format(Texture::F_depth_component32);
+        } else if (_fb_properties.get_depth_bits() > 16) {
+          tex->set_format(Texture::F_depth_component24);
+        } else if (_fb_properties.get_depth_bits() > 8) {
+          tex->set_format(Texture::F_depth_component16);
+        } else {
+          tex->set_format(Texture::F_depth_component);
+        }
+        break;
+      case RTP_depth_stencil:
+        tex->set_format(Texture::F_depth_stencil);
+        tex->set_component_type(Texture::T_unsigned_int_24_8);
+        break;
+      case RTP_aux_hrgba_0:
+      case RTP_aux_hrgba_1:
+      case RTP_aux_hrgba_2:
+      case RTP_aux_hrgba_3:
+        tex->set_format(Texture::F_rgba16);
+        break;
+      case RTP_aux_float_0:
+      case RTP_aux_float_1:
+      case RTP_aux_float_2:
+      case RTP_aux_float_3:
+        tex->set_format(Texture::F_rgba32);
+        tex->set_component_type(Texture::T_float);
+        break;
+      default:
+        tex->set_format(Texture::F_rgba);
+    }
+
+    // Create the OpenGL texture object.
     TextureContext *tc = tex->prepare_now(0, glgsg->get_prepared_objects(), glgsg);
     TextureContext *tc = tex->prepare_now(0, glgsg->get_prepared_objects(), glgsg);
     nassertv(tc != (TextureContext *)NULL);
     nassertv(tc != (TextureContext *)NULL);
     CLP(TextureContext) *gtc = DCAST(CLP(TextureContext), tc);
     CLP(TextureContext) *gtc = DCAST(CLP(TextureContext), tc);
     glgsg->update_texture(tc, true);
     glgsg->update_texture(tc, true);
 
 
-    _use_depth_stencil = false;
     if (attachpoint == GL_DEPTH_ATTACHMENT_EXT) {
     if (attachpoint == GL_DEPTH_ATTACHMENT_EXT) {
-      if ( _gsg->get_supports_depth_stencil() && tex->get_format() == Texture::F_depth_stencil ) {
-        tex->set_component_type(Texture::T_unsigned_int_24_8);
-        _use_depth_stencil = true;
-      }
 #ifndef OPENGLES
 #ifndef OPENGLES
       GLclampf priority = 1.0f;
       GLclampf priority = 1.0f;
       glPrioritizeTextures(1, &gtc->_index, &priority);
       glPrioritizeTextures(1, &gtc->_index, &priority);
 #endif
 #endif
-      glgsg->_glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,
-                                     target, gtc->_index, 0);
-      if (_use_depth_stencil) {
-        glgsg->_glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT,
-                                       target, gtc->_index, 0);
+      if (_rb_size_z == 1) {
+        if (target == GL_TEXTURE_3D) {
+          glgsg->_glFramebufferTexture3D(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,
+                                         target, gtc->_index, 0, layer);
+        } else if (target == GL_TEXTURE_2D_ARRAY_EXT) {
+          glgsg->_glFramebufferTextureLayer(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,
+                                            gtc->_index, 0, layer);
+        } else {
+          glgsg->_glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,
+                                         target, gtc->_index, 0);
+        }
+      } else {
+        glgsg->_glFramebufferTexture(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,
+                                     gtc->_index, 0);
       }
       }
 
 
-#ifndef OPENGLES
-      GLint depth_size = 0, stencil_size = 0;
+      GLint depth_size = 0;
       GLP(GetTexLevelParameteriv)(target, 0, GL_TEXTURE_DEPTH_SIZE, &depth_size);
       GLP(GetTexLevelParameteriv)(target, 0, GL_TEXTURE_DEPTH_SIZE, &depth_size);
-      if (_use_depth_stencil) {
-        GLP(GetTexLevelParameteriv)(target, 0, GL_TEXTURE_STENCIL_SIZE, &stencil_size);
-      }
       _fb_properties.set_depth_bits(depth_size);
       _fb_properties.set_depth_bits(depth_size);
-      _fb_properties.set_stencil_bits(stencil_size);
-#endif
 
 
-    } else {
-#ifdef OPENGLES
-      tex->set_format(Texture::F_rgba4);
-#else
-      if (glFormat == GL_RGBA16F_ARB) {
-        tex->set_format(Texture::F_rgba16);
-      } else if (glFormat == GL_RGBA32F_ARB) {
-        tex->set_format(Texture::F_rgba32);
-      } else {
-        tex->set_format(Texture::F_rgba);
+      if (slot == RTP_depth_stencil) {
+        if (_rb_size_z == 1) {
+          if (target == GL_TEXTURE_3D) {
+            glgsg->_glFramebufferTexture3D(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT,
+                                           target, gtc->_index, 0, layer);
+          } else if (target == GL_TEXTURE_2D_ARRAY_EXT) {
+            glgsg->_glFramebufferTextureLayer(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT,
+                                              gtc->_index, 0, layer);
+          } else {
+            glgsg->_glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT,
+                                           target, gtc->_index, 0);
+          }
+        } else {
+          glgsg->_glFramebufferTexture(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT,
+                                       gtc->_index, 0);
+        }
+
+        GLint stencil_size = 0;
+        GLP(GetTexLevelParameteriv)(target, 0, GL_TEXTURE_STENCIL_SIZE, &stencil_size);
+        _fb_properties.set_stencil_bits(stencil_size);
       }
       }
-#endif
 
 
+    } else {
 #ifndef OPENGLES
 #ifndef OPENGLES
       GLclampf priority = 1.0f;
       GLclampf priority = 1.0f;
       glPrioritizeTextures(1, &gtc->_index, &priority);
       glPrioritizeTextures(1, &gtc->_index, &priority);
 #endif
 #endif
       glgsg->update_texture(tc, true);
       glgsg->update_texture(tc, true);
-      glgsg->_glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, attachpoint,
-                                     target, gtc->_index, 0);
+      if (_rb_size_z == 1) {
+        if (target == GL_TEXTURE_3D) {
+          glgsg->_glFramebufferTexture3D(GL_FRAMEBUFFER_EXT, attachpoint,
+                                         target, gtc->_index, 0, layer);
+        } else if (target == GL_TEXTURE_2D_ARRAY_EXT) {
+          glgsg->_glFramebufferTextureLayer(GL_FRAMEBUFFER_EXT, attachpoint,
+                                            gtc->_index, 0, layer);
+        } else {
+          glgsg->_glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, attachpoint,
+                                         target, gtc->_index, 0);
+        }
+      } else {
+        glgsg->_glFramebufferTexture(GL_FRAMEBUFFER_EXT, attachpoint,
+                                     gtc->_index, 0);
+      }
 #ifndef OPENGLES
 #ifndef OPENGLES
       if (attachpoint == GL_COLOR_ATTACHMENT0_EXT) {
       if (attachpoint == GL_COLOR_ATTACHMENT0_EXT) {
         GLint red_size = 0, green_size = 0, blue_size = 0, alpha_size = 0;
         GLint red_size = 0, green_size = 0, blue_size = 0, alpha_size = 0;
@@ -536,28 +680,77 @@ bind_slot(int face, bool rb_resize, Texture **attach, RenderTexturePlane slot, G
 #endif
 #endif
     }
     }
 
 
-    _tex[slot] = tex;
-
     // If there was a renderbuffer bound to this slot, delete it.
     // If there was a renderbuffer bound to this slot, delete it.
     if (_rb[slot] != 0) {
     if (_rb[slot] != 0) {
       glgsg->_glDeleteRenderbuffers(1, &(_rb[slot]));
       glgsg->_glDeleteRenderbuffers(1, &(_rb[slot]));
       _rb[slot] = 0;
       _rb[slot] = 0;
     }
     }
 
 
-  } else {
-
-    // Disconnect from any texture that was previously bound to this slot.
-    _tex[slot] = 0;
+    report_my_gl_errors();
 
 
-#ifndef OPENGLES_2
-    // Check for the tricky case of a depth_stencil buffer:
-    // If we think we need one, but we have a texture bound in the depth slot,
-    // then we DON'T want to create a depth_stencil buffer here (because depth is
-    // a subset of depth_stencil).
-    if (( slot == RTP_depth_stencil ) && ( _gsg->get_supports_depth_stencil() != false ) &&
-        ( attach[RTP_depth] != NULL )) {
-        return;
+  } else {
+    // No texture to bind.  Instead, create a renderbuffer.
+    // Choose a suitable renderbuffer format based on the requirements.
+#ifdef OPENGLES
+    GLuint gl_format = GL_RGBA4;
+    switch (slot) {
+      case RTP_depth_stencil:
+        gl_format = GL_DEPTH_STENCIL_OES;
+        break;
+      case RTP_depth:
+        gl_format = GL_DEPTH_COMPONENT16;
+        break;
+      //case RTP_stencil:
+      //  gl_format = GL_STENCIL_INDEX8;
+      //  break
+      default:
+        if (_fb_properties.get_alpha_bits() == 0) {
+          gl_format = GL_RGB565;
+        } else if (_fb_properties.get_color_bits() == 15
+                && _fb_properties.get_alpha_bits() == 1) {
+          gl_format = GL_RGB5_A1;
+        }
     }
     }
+#else
+    GLuint gl_format = GL_RGBA;
+    switch (slot) {
+      case RTP_depth_stencil:
+        gl_format = GL_DEPTH_STENCIL_EXT;
+        break;
+      case RTP_depth:
+        if (_fb_properties.get_depth_bits() >= 32) {
+          gl_format = GL_DEPTH_COMPONENT32;
+        } else if (_fb_properties.get_depth_bits() > 16) {
+          gl_format = GL_DEPTH_COMPONENT24;
+        } else if (_fb_properties.get_depth_bits() > 8) {
+          gl_format = GL_DEPTH_COMPONENT16;
+        } else {
+          gl_format = GL_DEPTH_COMPONENT;
+        }
+        break;
+      case RTP_aux_rgba_0:
+      case RTP_aux_rgba_1:
+      case RTP_aux_rgba_2:
+      case RTP_aux_rgba_3:
+        gl_format = GL_RGBA;
+        break;
+      case RTP_aux_hrgba_0:
+      case RTP_aux_hrgba_1:
+      case RTP_aux_hrgba_2:
+      case RTP_aux_hrgba_3:
+        gl_format = GL_RGBA16F_ARB;
+        break;
+      case RTP_aux_float_0:
+      case RTP_aux_float_1:
+      case RTP_aux_float_2:
+      case RTP_aux_float_3:
+        gl_format = GL_RGBA32F_ARB;
+        break;
+      default:
+        if (_fb_properties.get_alpha_bits() == 0) {
+          gl_format = GL_RGB;
+        }
+    };
 #endif
 #endif
 
 
     // If there's no renderbuffer for this slot, create one.
     // If there's no renderbuffer for this slot, create one.
@@ -567,71 +760,51 @@ bind_slot(int face, bool rb_resize, Texture **attach, RenderTexturePlane slot, G
 
 
     // Allocate and bind the renderbuffer.
     // Allocate and bind the renderbuffer.
     glgsg->_glBindRenderbuffer(GL_RENDERBUFFER_EXT, _rb[slot]);
     glgsg->_glBindRenderbuffer(GL_RENDERBUFFER_EXT, _rb[slot]);
-    if (attachpoint == GL_DEPTH_ATTACHMENT_EXT) {
-#ifndef OPENGLES_2
-      if ( _gsg->get_supports_depth_stencil() != false ) {
-        if ( slot == RTP_depth_stencil ) {
-          glgsg->_glRenderbufferStorage(GL_RENDERBUFFER_EXT, GL_DEPTH_STENCIL_EXT,
-                                        _rb_size_x, _rb_size_y);
-          GLint depth_size = 0;
-          glgsg->_glGetRenderbufferParameteriv(GL_RENDERBUFFER_EXT, GL_RENDERBUFFER_DEPTH_SIZE_EXT, &depth_size);
-          _fb_properties.set_depth_bits(depth_size);
-          GLint stencil_size = 0;
-          glgsg->_glGetRenderbufferParameteriv(GL_RENDERBUFFER_EXT, GL_RENDERBUFFER_STENCIL_SIZE_EXT, &stencil_size);
-          _fb_properties.set_stencil_bits(stencil_size);
-
-          glgsg->_glBindRenderbuffer(GL_RENDERBUFFER_EXT, 0);
-
-          GLuint rb;
-
-          rb = _rb[slot];
-          if (_shared_depth_buffer) {
-            rb = _shared_depth_buffer -> _rb[slot];
-          }
+    if (slot == RTP_depth_stencil) {
+      // Allocate renderbuffer storage for depth stencil.
+      GLint depth_size = 0, stencil_size = 0;
+      glgsg->_glRenderbufferStorage(GL_RENDERBUFFER_EXT, gl_format, _rb_size_x, _rb_size_y);
+      glgsg->_glGetRenderbufferParameteriv(GL_RENDERBUFFER_EXT, GL_RENDERBUFFER_DEPTH_SIZE_EXT, &depth_size);
+      glgsg->_glGetRenderbufferParameteriv(GL_RENDERBUFFER_EXT, GL_RENDERBUFFER_STENCIL_SIZE_EXT, &stencil_size);
+      _fb_properties.set_depth_bits(depth_size);
+      _fb_properties.set_stencil_bits(stencil_size);
 
 
-          glgsg->_glFramebufferRenderbuffer(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,
-                                            GL_RENDERBUFFER_EXT, rb);
+      glgsg->_glBindRenderbuffer(GL_RENDERBUFFER_EXT, 0);
 
 
-          glgsg->_glFramebufferRenderbuffer(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT,
-                                            GL_RENDERBUFFER_EXT, rb);
-          return;
+      GLuint rb = _rb[slot];
+      if (_shared_depth_buffer) {
+        rb = _shared_depth_buffer->_rb[slot];
+      }
 
 
-        } else if ( slot == RTP_depth ) {
-          // This is the uber-tricky case, where we DON'T want to bind a depth buffer
-          // if there's already any form of depth_stencil buffer bound (because depth_stencil
-          // is a superset that includes depth).
-          if (( _rb[RTP_depth_stencil] != 0 ) || ( attach[RTP_depth_stencil] != NULL )) {
-            return;
-          }
+      glgsg->_glFramebufferRenderbuffer(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,
+                                        GL_RENDERBUFFER_EXT, rb);
 
 
-        }
-      }
+      glgsg->_glFramebufferRenderbuffer(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT,
+                                        GL_RENDERBUFFER_EXT, rb);
 
 
-      // We'll bail out before here if we set a depth_stencil buffer,
-      // or figure out that we're GOING to set a depth_stencil buffer.
+      report_my_gl_errors();
 
 
-      // If we get here, we're using the simple fallback case.
-#endif
-      glgsg->_glRenderbufferStorage(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT16,
-                                    _rb_size_x, _rb_size_y);
+    } else if (slot == RTP_depth) {
+      // Allocate renderbuffer storage for regular depth.
       GLint depth_size = 0;
       GLint depth_size = 0;
+      glgsg->_glRenderbufferStorage(GL_RENDERBUFFER_EXT, gl_format, _rb_size_x, _rb_size_y);
       glgsg->_glGetRenderbufferParameteriv(GL_RENDERBUFFER_EXT, GL_RENDERBUFFER_DEPTH_SIZE_EXT, &depth_size);
       glgsg->_glGetRenderbufferParameteriv(GL_RENDERBUFFER_EXT, GL_RENDERBUFFER_DEPTH_SIZE_EXT, &depth_size);
       _fb_properties.set_depth_bits(depth_size);
       _fb_properties.set_depth_bits(depth_size);
 
 
       glgsg->_glBindRenderbuffer(GL_RENDERBUFFER_EXT, 0);
       glgsg->_glBindRenderbuffer(GL_RENDERBUFFER_EXT, 0);
 
 
-      GLuint rb;
-
-      rb = _rb[slot];
+      GLuint rb = _rb[slot];
       if (_shared_depth_buffer) {
       if (_shared_depth_buffer) {
-        rb = _shared_depth_buffer -> _rb[slot];
+        rb = _shared_depth_buffer->_rb[slot];
       }
       }
 
 
       glgsg->_glFramebufferRenderbuffer(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,
       glgsg->_glFramebufferRenderbuffer(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,
                                         GL_RENDERBUFFER_EXT, rb);
                                         GL_RENDERBUFFER_EXT, rb);
+
+      report_my_gl_errors();
+
     } else {
     } else {
-      glgsg->_glRenderbufferStorage(GL_RENDERBUFFER_EXT, glFormat,
-                                    _rb_size_x, _rb_size_y);
+      glgsg->_glRenderbufferStorage(GL_RENDERBUFFER_EXT, gl_format, _rb_size_x, _rb_size_y);
       GLint red_size = 0, green_size = 0, blue_size = 0, alpha_size = 0;
       GLint red_size = 0, green_size = 0, blue_size = 0, alpha_size = 0;
       glgsg->_glGetRenderbufferParameteriv(GL_RENDERBUFFER_EXT, GL_RENDERBUFFER_RED_SIZE_EXT, &red_size);
       glgsg->_glGetRenderbufferParameteriv(GL_RENDERBUFFER_EXT, GL_RENDERBUFFER_RED_SIZE_EXT, &red_size);
       glgsg->_glGetRenderbufferParameteriv(GL_RENDERBUFFER_EXT, GL_RENDERBUFFER_GREEN_SIZE_EXT, &green_size);
       glgsg->_glGetRenderbufferParameteriv(GL_RENDERBUFFER_EXT, GL_RENDERBUFFER_GREEN_SIZE_EXT, &green_size);
@@ -643,10 +816,9 @@ bind_slot(int face, bool rb_resize, Texture **attach, RenderTexturePlane slot, G
       glgsg->_glFramebufferRenderbuffer(GL_FRAMEBUFFER_EXT, attachpoint,
       glgsg->_glFramebufferRenderbuffer(GL_FRAMEBUFFER_EXT, attachpoint,
                                         GL_RENDERBUFFER_EXT, _rb[slot]);
                                         GL_RENDERBUFFER_EXT, _rb[slot]);
 
 
+      report_my_gl_errors();
     }
     }
   }
   }
-
-  report_my_gl_errors();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -675,7 +847,7 @@ bind_slot_multisample(bool rb_resize, Texture **attach, RenderTexturePlane slot,
 
 
   if (attachpoint == GL_DEPTH_ATTACHMENT_EXT) {
   if (attachpoint == GL_DEPTH_ATTACHMENT_EXT) {
 #ifndef OPENGLES_2
 #ifndef OPENGLES_2
-    if (_gsg->get_supports_depth_stencil() && _use_depth_stencil) {
+    if (_use_depth_stencil) {
       glgsg->_glBindRenderbuffer(GL_RENDERBUFFER_EXT, _rbm[slot]);
       glgsg->_glBindRenderbuffer(GL_RENDERBUFFER_EXT, _rbm[slot]);
       if (_requested_coverage_samples) {
       if (_requested_coverage_samples) {
         glgsg->_glRenderbufferStorageMultisampleCoverage(GL_RENDERBUFFER_EXT, _requested_coverage_samples,
         glgsg->_glRenderbufferStorageMultisampleCoverage(GL_RENDERBUFFER_EXT, _requested_coverage_samples,
@@ -699,12 +871,17 @@ bind_slot_multisample(bool rb_resize, Texture **attach, RenderTexturePlane slot,
       glgsg->_glBindRenderbuffer(GL_RENDERBUFFER_EXT, _rbm[slot]);
       glgsg->_glBindRenderbuffer(GL_RENDERBUFFER_EXT, _rbm[slot]);
       GLuint format = GL_DEPTH_COMPONENT;
       GLuint format = GL_DEPTH_COMPONENT;
       if (tex) {
       if (tex) {
-        if (tex->get_format() == Texture::F_depth_component16)
+        switch (tex->get_format()) {
+          case Texture::F_depth_component16:
             format = GL_DEPTH_COMPONENT16;
             format = GL_DEPTH_COMPONENT16;
-        if (tex->get_format() == Texture::F_depth_component24)
+            break;
+          case Texture::F_depth_component24:
             format = GL_DEPTH_COMPONENT24;
             format = GL_DEPTH_COMPONENT24;
-        if (tex->get_format() == Texture::F_depth_component32)
+            break;
+          case Texture::F_depth_component32:
             format = GL_DEPTH_COMPONENT32;
             format = GL_DEPTH_COMPONENT32;
+            break;
+        }
       }
       }
       if (_requested_coverage_samples) {
       if (_requested_coverage_samples) {
         glgsg->_glRenderbufferStorageMultisampleCoverage(GL_RENDERBUFFER_EXT, _requested_coverage_samples,
         glgsg->_glRenderbufferStorageMultisampleCoverage(GL_RENDERBUFFER_EXT, _requested_coverage_samples,
@@ -726,29 +903,35 @@ bind_slot_multisample(bool rb_resize, Texture **attach, RenderTexturePlane slot,
     }
     }
   } else {
   } else {
     Texture *Tex = attach[slot];
     Texture *Tex = attach[slot];
-    GLuint glFormat = GL_RGBA;
+    GLuint gl_format = GL_RGBA;
 #ifndef OPENGLES
 #ifndef OPENGLES
     switch (slot) {
     switch (slot) {
       case RTP_aux_rgba_0:
       case RTP_aux_rgba_0:
       case RTP_aux_rgba_1:
       case RTP_aux_rgba_1:
       case RTP_aux_rgba_2:
       case RTP_aux_rgba_2:
       case RTP_aux_rgba_3:
       case RTP_aux_rgba_3:
-        glFormat = GL_RGBA;
+        gl_format = GL_RGBA;
         break;
         break;
       case RTP_aux_hrgba_0:
       case RTP_aux_hrgba_0:
       case RTP_aux_hrgba_1:
       case RTP_aux_hrgba_1:
       case RTP_aux_hrgba_2:
       case RTP_aux_hrgba_2:
       case RTP_aux_hrgba_3:
       case RTP_aux_hrgba_3:
-        glFormat = GL_RGBA16F_ARB;
+        gl_format = GL_RGBA16F_ARB;
+        break;
+      case RTP_aux_float_0:
+      case RTP_aux_float_1:
+      case RTP_aux_float_2:
+      case RTP_aux_float_3:
+        gl_format = GL_RGBA32F_ARB;
         break;
         break;
     };
     };
 #endif
 #endif
     glgsg->_glBindRenderbuffer(GL_RENDERBUFFER_EXT, _rbm[slot]);
     glgsg->_glBindRenderbuffer(GL_RENDERBUFFER_EXT, _rbm[slot]);
     if (_requested_coverage_samples) {
     if (_requested_coverage_samples) {
       glgsg->_glRenderbufferStorageMultisampleCoverage(GL_RENDERBUFFER_EXT, _requested_coverage_samples,
       glgsg->_glRenderbufferStorageMultisampleCoverage(GL_RENDERBUFFER_EXT, _requested_coverage_samples,
-                                    _requested_multisamples, glFormat, _rb_size_x, _rb_size_y);
+                                    _requested_multisamples, gl_format, _rb_size_x, _rb_size_y);
     } else {
     } else {
-      glgsg->_glRenderbufferStorageMultisample(GL_RENDERBUFFER_EXT, _requested_multisamples, glFormat,
+      glgsg->_glRenderbufferStorageMultisample(GL_RENDERBUFFER_EXT, _requested_multisamples, gl_format,
                                                _rb_size_x, _rb_size_y);
                                                _rb_size_x, _rb_size_y);
     }
     }
 #ifndef OPENGLES
 #ifndef OPENGLES
@@ -838,13 +1021,13 @@ end_frame(FrameMode mode, Thread *current_thread) {
 //       Access: Public, Virtual
 //       Access: Public, Virtual
 //  Description: Called internally when the window is in
 //  Description: Called internally when the window is in
 //               render-to-a-texture mode and we are in the process of
 //               render-to-a-texture mode and we are in the process of
-//               rendering the six faces of a cube map.  This should
+//               rendering the six layers of a cube map.  This should
 //               do whatever needs to be done to switch the buffer to
 //               do whatever needs to be done to switch the buffer to
 //               the indicated face.
 //               the indicated face.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void CLP(GraphicsBuffer)::
 void CLP(GraphicsBuffer)::
 select_cube_map(int cube_map_index) {
 select_cube_map(int cube_map_index) {
-  nassertv(cube_map_index >=0 && cube_map_index < _num_faces);
+  nassertv(cube_map_index >= 0 && cube_map_index < _fbo.size());
 
 
   if (_active_cube_map_index != -1) {
   if (_active_cube_map_index != -1) {
     // Resolve the multisample rendering for the previous face.
     // Resolve the multisample rendering for the previous face.
@@ -891,23 +1074,40 @@ open_buffer() {
 
 
   // Describe the framebuffer properties of the FBO.
   // Describe the framebuffer properties of the FBO.
   //
   //
-  // The rule is that the properties should be as close
-  // as possible to those requested, subject to the limits
-  // of the implementation.  This particular implementation
-  // is fairly limited.  But that's okay, we just have to
-  // tell the truth about what we actually provide by setting
-  // the _fb_properties accurately.
-
-  _fb_properties.set_depth_bits(1);
-  _fb_properties.set_color_bits(1);
-  _fb_properties.set_alpha_bits(_host->get_fb_properties().get_alpha_bits());
-  if (_gsg->get_supports_depth_stencil()) {
-    _fb_properties.set_stencil_bits(1);
-  } else {
+  // Unfortunately, we can't possibly predict which formats
+  // the implementation will allow us to use at this point, so
+  // we'll just have to make some guesses and parrot the rest
+  // of the properties back to the user.
+  // When we actually attach the textures, we'll update the
+  // properties more appropriately.
+  // This is probably safe, as we can usually bind just about
+  // any supported texture format to the FBO.
+
+  // Rounding the depth bits is not spectacular, but at least we're
+  // telling the user *something* about what we're going to get.
+  if (_fb_properties.get_depth_bits() >= 32) {
+    _fb_properties.set_depth_bits(32);
+  } else if (_fb_properties.get_depth_bits() > 16) {
+    _fb_properties.set_depth_bits(24);
+  } else if (_fb_properties.get_depth_bits() > 8) {
+    _fb_properties.set_depth_bits(16);
+  }
+
+  // We're not going to get more than this, ever.
+  if (_fb_properties.get_color_bits() > 96) {
+    _fb_properties.set_color_bits(96);
+  }
+  if (_fb_properties.get_alpha_bits() > 32) {
+    _fb_properties.set_alpha_bits(32);
+  }
+
+  if (!_gsg->get_supports_depth_stencil()) {
+    // At least we know we won't be getting stencil bits.
     _fb_properties.set_stencil_bits(0);
     _fb_properties.set_stencil_bits(0);
   }
   }
   _fb_properties.set_accum_bits(0);
   _fb_properties.set_accum_bits(0);
   _fb_properties.set_multisamples(_host->get_fb_properties().get_multisamples());
   _fb_properties.set_multisamples(_host->get_fb_properties().get_multisamples());
+
   _fb_properties.set_back_buffers(0);
   _fb_properties.set_back_buffers(0);
   _fb_properties.set_indexed_color(0);
   _fb_properties.set_indexed_color(0);
   _fb_properties.set_rgb_color(1);
   _fb_properties.set_rgb_color(1);
@@ -918,6 +1118,7 @@ open_buffer() {
   _is_valid = true;
   _is_valid = true;
   _needs_rebuild = true;
   _needs_rebuild = true;
   report_my_gl_errors();
   report_my_gl_errors();
+
   return true;
   return true;
 }
 }
 
 
@@ -963,10 +1164,10 @@ close_buffer() {
   report_my_gl_errors();
   report_my_gl_errors();
 
 
   // Delete the FBO itself.
   // Delete the FBO itself.
-  for (int face = 0; face < _num_faces; ++face) {
-    glgsg->_glDeleteFramebuffers(1, &_fbo[face]);
+  for (int i = 0; i < _fbo.size(); ++i) {
+    glgsg->_glDeleteFramebuffers(1, &_fbo[i]);
   }
   }
-  _num_faces = 0;
+  _fbo.clear();
 
 
   report_my_gl_errors();
   report_my_gl_errors();
 
 
@@ -994,34 +1195,34 @@ share_depth_buffer(GraphicsOutput *graphics_output) {
   if (this != input_graphics_output && input_graphics_output) {
   if (this != input_graphics_output && input_graphics_output) {
 
 
     state = true;
     state = true;
-    this -> unshare_depth_buffer();
+    this->unshare_depth_buffer();
 
 
     // check buffer sizes
     // check buffer sizes
-    if (this -> get_x_size() != input_graphics_output -> get_x_size()) {
-      GLCAT.error() << "share_depth_buffer: non matching width \n";
+    if (this->get_x_size() != input_graphics_output->get_x_size()) {
+      GLCAT.error() << "share_depth_buffer: non-matching width\n";
       state = false;
       state = false;
     }
     }
 
 
-    if (this -> get_y_size() != input_graphics_output -> get_y_size()) {
-      GLCAT.error() << "share_depth_buffer: non matching height \n";
+    if (this->get_y_size() != input_graphics_output->get_y_size()) {
+      GLCAT.error() << "share_depth_buffer: non-matching height\n";
       state = false;
       state = false;
     }
     }
 
 
     // Check multisample compatibility.
     // Check multisample compatibility.
-    if ( this->get_multisample_count() != input_graphics_output->get_multisample_count() ) {
-      GLCAT.error() << "share_depth_buffer: non matching multisamples \n";
+    if (this->get_multisample_count() != input_graphics_output->get_multisample_count()) {
+      GLCAT.error() << "share_depth_buffer: non-matching multisamples\n";
       state = false;
       state = false;
     }
     }
 
 
-    if ( this->get_coverage_sample_count() != input_graphics_output->get_coverage_sample_count() ) {
-      GLCAT.error() << "share_depth_buffer: non matching multisamples \n";
+    if (this->get_coverage_sample_count() != input_graphics_output->get_coverage_sample_count()) {
+      GLCAT.error() << "share_depth_buffer: non-matching multisamples\n";
       state = false;
       state = false;
     }
     }
 
 
     if (state) {
     if (state) {
       // let the input GraphicsOutput know that there is an object
       // let the input GraphicsOutput know that there is an object
       // sharing its depth buffer
       // sharing its depth buffer
-      input_graphics_output -> register_shared_depth_buffer(this);
+      input_graphics_output->register_shared_depth_buffer(this);
       _shared_depth_buffer = input_graphics_output;
       _shared_depth_buffer = input_graphics_output;
       state = true;
       state = true;
     }
     }
@@ -1041,7 +1242,7 @@ unshare_depth_buffer() {
   if (_shared_depth_buffer) {
   if (_shared_depth_buffer) {
     // let the GraphicsOutput know that this object is no longer
     // let the GraphicsOutput know that this object is no longer
     // sharing its depth buffer
     // sharing its depth buffer
-    _shared_depth_buffer -> unregister_shared_depth_buffer(this);
+    _shared_depth_buffer->unregister_shared_depth_buffer(this);
     _shared_depth_buffer = 0;
     _shared_depth_buffer = 0;
     _needs_rebuild = true;
     _needs_rebuild = true;
   }
   }
@@ -1139,13 +1340,15 @@ resolve_multisamples() {
   CLP(GraphicsStateGuardian) *glgsg;
   CLP(GraphicsStateGuardian) *glgsg;
   DCAST_INTO_V(glgsg, _gsg);
   DCAST_INTO_V(glgsg, _gsg);
 
 
+  nassertv(_fbo.size() > 0);
+
   glgsg->report_my_gl_errors();
   glgsg->report_my_gl_errors();
   GLuint fbo = _fbo[0];
   GLuint fbo = _fbo[0];
   if (_active_cube_map_index != -1) {
   if (_active_cube_map_index != -1) {
     fbo = _fbo[_active_cube_map_index];
     fbo = _fbo[_active_cube_map_index];
   }
   }
-  glgsg->_glBindFramebuffer( GL_DRAW_FRAMEBUFFER_EXT, fbo );
-  glgsg->_glBindFramebuffer( GL_READ_FRAMEBUFFER_EXT, _fbo_multisample );
+  glgsg->_glBindFramebuffer(GL_DRAW_FRAMEBUFFER_EXT, fbo);
+  glgsg->_glBindFramebuffer(GL_READ_FRAMEBUFFER_EXT, _fbo_multisample);
   
   
   // If the depth buffer is shared, resolve it only on the last to render FBO.
   // If the depth buffer is shared, resolve it only on the last to render FBO.
   int do_depth_blit = 0;
   int do_depth_blit = 0;

+ 2 - 3
panda/src/glstuff/glGraphicsBuffer_src.h

@@ -103,8 +103,7 @@ private:
 
 
   // We create one FBO for each cube map face we'll be rendering to.
   // We create one FBO for each cube map face we'll be rendering to.
   // If we aren't rendering to any cube maps, we use only _fbo[0].
   // If we aren't rendering to any cube maps, we use only _fbo[0].
-  GLuint _fbo[6];
-  int _num_faces;
+  pvector<GLuint> _fbo;
 
 
   // For multisample we render first to a multisample buffer, then
   // For multisample we render first to a multisample buffer, then
   // filter it to _fbo[face] at the end of the frame.
   // filter it to _fbo[face] at the end of the frame.
@@ -115,6 +114,7 @@ private:
 
 
   int         _rb_size_x;
   int         _rb_size_x;
   int         _rb_size_y;
   int         _rb_size_y;
+  int         _rb_size_z;
 
 
   // The texture or render buffer bound to each plane.
   // The texture or render buffer bound to each plane.
   PT(Texture) _tex[RTP_COUNT];
   PT(Texture) _tex[RTP_COUNT];
@@ -158,4 +158,3 @@ private:
 };
 };
 
 
 #include "glGraphicsBuffer_src.I"
 #include "glGraphicsBuffer_src.I"
-

+ 142 - 68
panda/src/glstuff/glGraphicsStateGuardian_src.cxx

@@ -581,6 +581,8 @@ reset() {
       get_extension_func(GLPREFIX_QUOTED, "TexImage3D");
       get_extension_func(GLPREFIX_QUOTED, "TexImage3D");
     _glTexSubImage3D = (PFNGLTEXSUBIMAGE3DPROC)
     _glTexSubImage3D = (PFNGLTEXSUBIMAGE3DPROC)
       get_extension_func(GLPREFIX_QUOTED, "TexSubImage3D");
       get_extension_func(GLPREFIX_QUOTED, "TexSubImage3D");
+    _glCopyTexSubImage3D = (PFNGLCOPYTEXSUBIMAGE3DPROC)
+      get_extension_func(GLPREFIX_QUOTED, "CopyTexSubImage3D");
 
 
   } else if (has_extension("GL_EXT_texture3D")) {
   } else if (has_extension("GL_EXT_texture3D")) {
     _supports_3d_texture = true;
     _supports_3d_texture = true;
@@ -589,14 +591,23 @@ reset() {
       get_extension_func(GLPREFIX_QUOTED, "TexImage3DEXT");
       get_extension_func(GLPREFIX_QUOTED, "TexImage3DEXT");
     _glTexSubImage3D = (PFNGLTEXSUBIMAGE3DPROC)
     _glTexSubImage3D = (PFNGLTEXSUBIMAGE3DPROC)
       get_extension_func(GLPREFIX_QUOTED, "TexSubImage3DEXT");
       get_extension_func(GLPREFIX_QUOTED, "TexSubImage3DEXT");
-  } else if (has_extension("GL_OES_texture3D") || has_extension("GL_OES_texture_3D")) {
+
+    _glCopyTexSubImage3D = NULL;
+    if (has_extension("GL_EXT_copy_texture")) {
+      _glCopyTexSubImage3D = (PFNGLCOPYTEXSUBIMAGE3DPROC)
+        get_extension_func(GLPREFIX_QUOTED, "CopyTexSubImage3DEXT");
+    }
+
+  } else if (has_extension("GL_OES_texture_3D")) {
     _supports_3d_texture = true;
     _supports_3d_texture = true;
 
 
     _glTexImage3D = (PFNGLTEXIMAGE3DPROC_P)
     _glTexImage3D = (PFNGLTEXIMAGE3DPROC_P)
       get_extension_func(GLPREFIX_QUOTED, "TexImage3DOES");
       get_extension_func(GLPREFIX_QUOTED, "TexImage3DOES");
     _glTexSubImage3D = (PFNGLTEXSUBIMAGE3DPROC)
     _glTexSubImage3D = (PFNGLTEXSUBIMAGE3DPROC)
       get_extension_func(GLPREFIX_QUOTED, "TexSubImage3DOES");
       get_extension_func(GLPREFIX_QUOTED, "TexSubImage3DOES");
-    
+    _glCopyTexSubImage3D = (PFNGLCOPYTEXSUBIMAGE3DPROC)
+      get_extension_func(GLPREFIX_QUOTED, "CopyTexSubImage3DOES");
+
 #ifdef OPENGLES_2
 #ifdef OPENGLES_2
     _glFramebufferTexture3D = (PFNGLFRAMEBUFFERTEXTURE3DOES)
     _glFramebufferTexture3D = (PFNGLFRAMEBUFFERTEXTURE3DOES)
       get_extension_func(GLPREFIX_QUOTED, "FramebufferTexture3DOES");
       get_extension_func(GLPREFIX_QUOTED, "FramebufferTexture3DOES");
@@ -611,9 +622,18 @@ reset() {
     }
     }
   }
   }
 
 
+  _supports_2d_texture_array = false;
 #ifndef OPENGLES
 #ifndef OPENGLES
   _supports_2d_texture_array = has_extension("GL_EXT_texture_array");
   _supports_2d_texture_array = has_extension("GL_EXT_texture_array");
+  if (_supports_2d_texture_array) {
+    _glFramebufferTextureLayer = (PFNGLFRAMEBUFFERTEXTURELAYERPROC)
+      get_extension_func(GLPREFIX_QUOTED, "FramebufferTextureLayerEXT");
+  } else {
+    // ARB_geometry_shader4 also provides a version.
+    _glFramebufferTextureLayer = NULL;
+  }
 #endif
 #endif
+
 #ifdef OPENGLES_2
 #ifdef OPENGLES_2
   _supports_cube_map = true;
   _supports_cube_map = true;
 #else
 #else
@@ -1048,6 +1068,13 @@ reset() {
     if (_supports_geometry_shaders) {
     if (_supports_geometry_shaders) {
       _glProgramParameteri = (PFNGLPROGRAMPARAMETERIPROC)
       _glProgramParameteri = (PFNGLPROGRAMPARAMETERIPROC)
         get_extension_func(GLPREFIX_QUOTED, "ProgramParameteri");
         get_extension_func(GLPREFIX_QUOTED, "ProgramParameteri");
+      _glFramebufferTexture = (PFNGLFRAMEBUFFERTEXTUREARBPROC)
+        get_extension_func(GLPREFIX_QUOTED, "FramebufferTextureARB");
+
+      if (_glFramebufferTextureLayer == NULL) {
+        _glFramebufferTextureLayer = (PFNGLFRAMEBUFFERTEXTURELAYERPROC)
+          get_extension_func(GLPREFIX_QUOTED, "FramebufferTextureLayerARB");
+      }
     }
     }
     if (_supports_tessellation_shaders) {
     if (_supports_tessellation_shaders) {
       _glPatchParameteri = (PFNGLPATCHPARAMETERIPROC)
       _glPatchParameteri = (PFNGLPATCHPARAMETERIPROC)
@@ -1113,12 +1140,6 @@ reset() {
   _glCheckFramebufferStatus = glCheckFramebufferStatus;
   _glCheckFramebufferStatus = glCheckFramebufferStatus;
   _glFramebufferTexture1D = NULL;
   _glFramebufferTexture1D = NULL;
   _glFramebufferTexture2D = glFramebufferTexture2D;
   _glFramebufferTexture2D = glFramebufferTexture2D;
-  if (has_extension("GL_OES_texture3D")) {
-    _glFramebufferTexture3D = (PFNGLFRAMEBUFFERTEXTURE3DOES)
-      get_extension_func(GLPREFIX_QUOTED, "FramebufferTexture3DOES");
-  } else {
-    _glFramebufferTexture3D = NULL;
-  }
   _glFramebufferRenderbuffer = glFramebufferRenderbuffer;
   _glFramebufferRenderbuffer = glFramebufferRenderbuffer;
   _glGetFramebufferAttachmentParameteriv = glGetFramebufferAttachmentParameteriv;
   _glGetFramebufferAttachmentParameteriv = glGetFramebufferAttachmentParameteriv;
   _glGenerateMipmap = glGenerateMipmap;
   _glGenerateMipmap = glGenerateMipmap;
@@ -1189,7 +1210,6 @@ reset() {
     _glFramebufferTexture1D = NULL;
     _glFramebufferTexture1D = NULL;
     _glFramebufferTexture2D = (PFNGLFRAMEBUFFERTEXTURE2DOESPROC)
     _glFramebufferTexture2D = (PFNGLFRAMEBUFFERTEXTURE2DOESPROC)
       get_extension_func(GLPREFIX_QUOTED, "FramebufferTexture2DOES");
       get_extension_func(GLPREFIX_QUOTED, "FramebufferTexture2DOES");
-    _glFramebufferTexture3D = NULL;
     _glFramebufferRenderbuffer = (PFNGLFRAMEBUFFERRENDERBUFFEROESPROC)
     _glFramebufferRenderbuffer = (PFNGLFRAMEBUFFERRENDERBUFFEROESPROC)
       get_extension_func(GLPREFIX_QUOTED, "FramebufferRenderbufferOES");
       get_extension_func(GLPREFIX_QUOTED, "FramebufferRenderbufferOES");
     _glGetFramebufferAttachmentParameteriv = (PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVOESPROC)
     _glGetFramebufferAttachmentParameteriv = (PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVOESPROC)
@@ -4170,8 +4190,8 @@ make_geom_munger(const RenderState *state, Thread *current_thread) {
 //  Description: Copy the pixels within the indicated display
 //  Description: Copy the pixels within the indicated display
 //               region from the framebuffer into texture memory.
 //               region from the framebuffer into texture memory.
 //
 //
-//               If z > -1, it is the cube map index into which to
-//               copy.
+//               If z > -1, it is the cube map index or layer index
+//               into which to copy.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 bool CLP(GraphicsStateGuardian)::
 bool CLP(GraphicsStateGuardian)::
 framebuffer_copy_to_texture(Texture *tex, int z, const DisplayRegion *dr,
 framebuffer_copy_to_texture(Texture *tex, int z, const DisplayRegion *dr,
@@ -4184,7 +4204,7 @@ framebuffer_copy_to_texture(Texture *tex, int z, const DisplayRegion *dr,
   
   
   int xo, yo, w, h;
   int xo, yo, w, h;
   dr->get_region_pixels(xo, yo, w, h);
   dr->get_region_pixels(xo, yo, w, h);
-  tex->set_size_padded(w, h);
+  tex->set_size_padded(w, h, tex->get_z_size());
 
 
   if (tex->get_compression() == Texture::CM_default) {
   if (tex->get_compression() == Texture::CM_default) {
     // Unless the user explicitly turned on texture compression, turn
     // Unless the user explicitly turned on texture compression, turn
@@ -4194,16 +4214,41 @@ framebuffer_copy_to_texture(Texture *tex, int z, const DisplayRegion *dr,
 
 
   // Sanity check everything.
   // Sanity check everything.
   if (z >= 0) {
   if (z >= 0) {
-    if (!_supports_cube_map) {
+    if (z >= tex->get_z_size()) {
+      // This can happen, when textures with different layer counts
+      // are attached to a buffer.  We simply ignore this if it happens.
       return false;
       return false;
     }
     }
-    nassertr(z < 6, false);
-    nassertr(tex->get_texture_type() == Texture::TT_cube_map, false);
+
     if ((w != tex->get_x_size()) ||
     if ((w != tex->get_x_size()) ||
-        (h != tex->get_y_size()) ||
-        (w != h)) {
+        (h != tex->get_y_size())) {
       return false;
       return false;
     }
     }
+
+    if (tex->get_texture_type() == Texture::TT_cube_map) {
+      if (!_supports_cube_map) {
+        return false;
+      }
+
+      nassertr(z < 6, false);
+      if (w != h) {
+        return false;
+      }
+
+    } else if (tex->get_texture_type() == Texture::TT_3d_texture) {
+      if (!_supports_3d_texture) {
+        return false;
+      }
+
+    } else if (tex->get_texture_type() == Texture::TT_2d_texture_array) {
+      if (!_supports_2d_texture_array) {
+        return false;
+      }
+
+    } else {
+      GLCAT.error()
+        << "Don't know how to copy framebuffer to texture " << *tex << "\n";
+    }
   } else {
   } else {
     nassertr(tex->get_texture_type() == Texture::TT_2d_texture, false);
     nassertr(tex->get_texture_type() == Texture::TT_2d_texture, false);
   }
   }
@@ -4243,6 +4288,7 @@ framebuffer_copy_to_texture(Texture *tex, int z, const DisplayRegion *dr,
   GLint internal_format = get_internal_image_format(tex);
   GLint internal_format = get_internal_image_format(tex);
   int width = tex->get_x_size();
   int width = tex->get_x_size();
   int height = tex->get_y_size();
   int height = tex->get_y_size();
+  int depth = tex->get_z_size();
 
 
   bool uses_mipmaps = tex->uses_mipmaps() && !CLP(ignore_mipmaps);
   bool uses_mipmaps = tex->uses_mipmaps() && !CLP(ignore_mipmaps);
   if (uses_mipmaps) {
   if (uses_mipmaps) {
@@ -4260,34 +4306,52 @@ framebuffer_copy_to_texture(Texture *tex, int z, const DisplayRegion *dr,
   }
   }
 
 
   bool new_image = needs_reload || gtc->was_image_modified();
   bool new_image = needs_reload || gtc->was_image_modified();
+
+  if (z >= 0) {
+    if (target == GL_TEXTURE_CUBE_MAP) {
+      // Copy to a cube map face, which is treated as a 2D texture.
+      target = GL_TEXTURE_CUBE_MAP_POSITIVE_X + z;
+      depth = 1;
+      z = -1;
+
+      // Cube map faces seem to have trouble with CopyTexSubImage, so we
+      // always reload the image.
+      new_image = true;
+    }
+  }
+
   if (!gtc->_already_applied ||
   if (!gtc->_already_applied ||
       internal_format != gtc->_internal_format ||
       internal_format != gtc->_internal_format ||
       uses_mipmaps != gtc->_uses_mipmaps ||
       uses_mipmaps != gtc->_uses_mipmaps ||
       width != gtc->_width ||
       width != gtc->_width ||
       height != gtc->_height ||
       height != gtc->_height ||
-      1 != gtc->_depth) {
+      depth != gtc->_depth) {
     // If the texture properties have changed, we need to reload the
     // If the texture properties have changed, we need to reload the
     // image.
     // image.
     new_image = true;
     new_image = true;
   }
   }
-  if (z >= 0) {
-    // Copy to a cube map face.
-    target = GL_TEXTURE_CUBE_MAP_POSITIVE_X + z;
 
 
-    // Cube map faces seem to have trouble with CopyTexSubImage, so we
-    // always reload the image.
-    new_image = true;
-  }
+  if (z >= 0) {
+    if (new_image) {
+      // These won't be used because we pass a NULL image, but we still
+      // have to specify them.  Might as well use the actual values.
+      GLint external_format = get_external_image_format(tex);
+      GLint component_type = get_component_type(tex->get_component_type());
+      _glTexImage3D(target, 0, internal_format, width, height, depth, 0, external_format, component_type, NULL);
+    }
 
 
-  if (new_image) {
-    // We have to create a new image.
-    // It seems that OpenGL accepts a size higher than the framebuffer,
-    // but if we run into trouble we'll have to replace this with
-    // something smarter.
-    GLP(CopyTexImage2D)(target, 0, internal_format, xo, yo, width, height, 0);
+    _glCopyTexSubImage3D(target, 0, 0, 0, z, xo, yo, w, h);
   } else {
   } else {
-    // We can overlay the existing image.
-    GLP(CopyTexSubImage2D)(target, 0, 0, 0, xo, yo, w, h);
+    if (new_image) {
+      // We have to create a new image.
+      // It seems that OpenGL accepts a size higher than the framebuffer,
+      // but if we run into trouble we'll have to replace this with
+      // something smarter.
+      GLP(CopyTexImage2D)(target, 0, internal_format, xo, yo, width, height, 0);
+    } else {
+      // We can overlay the existing image.
+      GLP(CopyTexSubImage2D)(target, 0, 0, 0, xo, yo, w, h);
+    }
   }
   }
 
 
   gtc->_already_applied = true;
   gtc->_already_applied = true;
@@ -4295,7 +4359,7 @@ framebuffer_copy_to_texture(Texture *tex, int z, const DisplayRegion *dr,
   gtc->_internal_format = internal_format;
   gtc->_internal_format = internal_format;
   gtc->_width = width;
   gtc->_width = width;
   gtc->_height = height;
   gtc->_height = height;
-  gtc->_depth = 1;
+  gtc->_depth = depth;
 
 
   gtc->mark_loaded();
   gtc->mark_loaded();
   gtc->enqueue_lru(&_prepared_objects->_graphics_memory_lru);
   gtc->enqueue_lru(&_prepared_objects->_graphics_memory_lru);
@@ -4373,6 +4437,7 @@ framebuffer_copy_to_ram(Texture *tex, int z, const DisplayRegion *dr,
 
 
   Texture::TextureType texture_type;
   Texture::TextureType texture_type;
   int z_size;
   int z_size;
+  //TODO: should be extended to support 3D textures and 2D arrays.
   if (z >= 0) {
   if (z >= 0) {
     texture_type = Texture::TT_cube_map;
     texture_type = Texture::TT_cube_map;
     z_size = 6;
     z_size = 6;
@@ -4386,6 +4451,7 @@ framebuffer_copy_to_ram(Texture *tex, int z, const DisplayRegion *dr,
       tex->get_component_type() != component_type ||
       tex->get_component_type() != component_type ||
       tex->get_format() != format ||
       tex->get_format() != format ||
       tex->get_texture_type() != texture_type) {
       tex->get_texture_type() != texture_type) {
+
     // Re-setup the texture; its properties have changed.
     // Re-setup the texture; its properties have changed.
     tex->setup_texture(texture_type, w, h, z_size,
     tex->setup_texture(texture_type, w, h, z_size,
                        component_type, format);
                        component_type, format);
@@ -5681,6 +5747,7 @@ get_extension_func(const char *prefix, const char *name) {
     { "DrawRangeElements", (void *)&GLP(DrawRangeElements) },
     { "DrawRangeElements", (void *)&GLP(DrawRangeElements) },
     { "TexImage3D", (void *)&GLP(TexImage3D) },
     { "TexImage3D", (void *)&GLP(TexImage3D) },
     { "TexSubImage3D", (void *)&GLP(TexSubImage3D) },
     { "TexSubImage3D", (void *)&GLP(TexSubImage3D) },
+    { "CopyTexSubImage3D", (void *)&GLP(CopyTexSubImage3D) },
 #endif
 #endif
 #ifdef EXPECT_GL_VERSION_1_3
 #ifdef EXPECT_GL_VERSION_1_3
     { "ActiveTexture", (void *)&GLP(ActiveTexture) },
     { "ActiveTexture", (void *)&GLP(ActiveTexture) },
@@ -6308,15 +6375,7 @@ get_external_image_format(Texture *tex) const {
   case Texture::F_depth_component:
   case Texture::F_depth_component:
     return GL_DEPTH_COMPONENT;
     return GL_DEPTH_COMPONENT;
   case Texture::F_depth_stencil:
   case Texture::F_depth_stencil:
-#ifndef OPENGLES_2
-    if (CLP(force_depth_stencil)) {
-      return GL_DEPTH_STENCIL_EXT;
-    } else {
-#endif
-      return GL_DEPTH_COMPONENT;
-#ifndef OPENGLES_2
-    }
-#endif
+    return _supports_depth_stencil ? GL_DEPTH_STENCIL_EXT : GL_DEPTH_COMPONENT;
 #ifndef OPENGLES
 #ifndef OPENGLES
   case Texture::F_red:
   case Texture::F_red:
     return GL_RED;
     return GL_RED;
@@ -6522,7 +6581,11 @@ get_internal_image_format(Texture *tex) const {
     return GL_COLOR_INDEX;
     return GL_COLOR_INDEX;
 #endif
 #endif
   case Texture::F_depth_component:
   case Texture::F_depth_component:
-    return GL_DEPTH_COMPONENT;
+    if (tex->get_component_type() == Texture::T_float) {
+      return GL_DEPTH_COMPONENT32F;
+    } else {
+      return GL_DEPTH_COMPONENT;
+    }
   case Texture::F_depth_component16:
   case Texture::F_depth_component16:
 #ifdef OPENGLES_1
 #ifdef OPENGLES_1
     return GL_DEPTH_COMPONENT16_OES;
     return GL_DEPTH_COMPONENT16_OES;
@@ -6537,7 +6600,11 @@ get_internal_image_format(Texture *tex) const {
 #else
 #else
     return GL_DEPTH_COMPONENT24;
     return GL_DEPTH_COMPONENT24;
   case Texture::F_depth_component32:
   case Texture::F_depth_component32:
-    return GL_DEPTH_COMPONENT32;
+    if (tex->get_component_type() == Texture::T_float) {
+      return GL_DEPTH_COMPONENT32F;
+    } else {
+      return GL_DEPTH_COMPONENT32;
+    }
 #endif
 #endif
   case Texture::F_depth_stencil:
   case Texture::F_depth_stencil:
 #ifndef OPENGLES_2
 #ifndef OPENGLES_2
@@ -7755,15 +7822,15 @@ update_standard_texture_bindings() {
     TextureContext *tc = texture->prepare_now(view, _prepared_objects, this);
     TextureContext *tc = texture->prepare_now(view, _prepared_objects, this);
     if (tc == (TextureContext *)NULL) {
     if (tc == (TextureContext *)NULL) {
       // Something wrong with this texture; skip it.
       // Something wrong with this texture; skip it.
-      break;
+      continue;
     }
     }
     
     
 #ifndef OPENGLES_2
 #ifndef OPENGLES_2
     // Then, turn on the current texture mode.
     // Then, turn on the current texture mode.
     GLenum target = get_texture_target(texture->get_texture_type());
     GLenum target = get_texture_target(texture->get_texture_type());
-    if (target == GL_NONE) {
+    if (target == GL_NONE || target == GL_TEXTURE_2D_ARRAY_EXT) {
       // Unsupported texture mode.
       // Unsupported texture mode.
-      break;
+      continue;
     }
     }
     GLP(Enable)(target);
     GLP(Enable)(target);
 #endif
 #endif
@@ -7772,7 +7839,7 @@ update_standard_texture_bindings() {
 #ifndef OPENGLES_2
 #ifndef OPENGLES_2
       GLP(Disable)(target);
       GLP(Disable)(target);
 #endif
 #endif
-      break;
+      continue;
     }
     }
     
     
     if (stage->involves_color_scale() && _color_scale_enabled) {
     if (stage->involves_color_scale() && _color_scale_enabled) {
@@ -8635,7 +8702,7 @@ upload_texture(CLP(TextureContext) *gtc, bool force) {
   if (!get_supports_compressed_texture_format(image_compression)) {
   if (!get_supports_compressed_texture_format(image_compression)) {
     image = tex->get_uncompressed_ram_image();
     image = tex->get_uncompressed_ram_image();
     image_compression = Texture::CM_off;
     image_compression = Texture::CM_off;
-  } 
+  }
 
 
   if (GLCAT.is_debug()) {
   if (GLCAT.is_debug()) {
     if (image.is_null()) {
     if (image.is_null()) {
@@ -9010,7 +9077,7 @@ upload_texture_image(CLP(TextureContext) *gtc,
     if (GLCAT.is_debug()) {
     if (GLCAT.is_debug()) {
       GLCAT.debug()
       GLCAT.debug()
         << "subloading existing texture object, " << width << " x " << height
         << "subloading existing texture object, " << width << " x " << height
-        << " x " << depth << ", mipmaps " << num_ram_mipmap_levels
+        << " x " << depth << ", z = " << z << ", mipmaps " << num_ram_mipmap_levels
         << ", uses_mipmaps = " << uses_mipmaps << "\n";
         << ", uses_mipmaps = " << uses_mipmaps << "\n";
     }
     }
 
 
@@ -9142,31 +9209,37 @@ upload_texture_image(CLP(TextureContext) *gtc,
     if (GLCAT.is_debug()) {
     if (GLCAT.is_debug()) {
       GLCAT.debug()
       GLCAT.debug()
         << "loading new texture object, " << width << " x " << height
         << "loading new texture object, " << width << " x " << height
-        << " x " << depth << ", mipmaps " << num_ram_mipmap_levels 
+        << " x " << depth << ", z = " << z << ", mipmaps " << num_ram_mipmap_levels 
         << ", uses_mipmaps = " << uses_mipmaps << "\n";
         << ", uses_mipmaps = " << uses_mipmaps << "\n";
     }
     }
 
 
     if (num_ram_mipmap_levels == 0) {
     if (num_ram_mipmap_levels == 0) {
 #ifndef OPENGLES_2
 #ifndef OPENGLES_2
       if ((external_format == GL_DEPTH_STENCIL_EXT) && get_supports_depth_stencil()) {
       if ((external_format == GL_DEPTH_STENCIL_EXT) && get_supports_depth_stencil()) {
-        GLP(TexImage2D)(page_target, 0, GL_DEPTH_STENCIL_EXT,
-                        width, height, 0, GL_DEPTH_STENCIL_EXT, 
 #ifdef OPENGLES_1
 #ifdef OPENGLES_1
-                        GL_UNSIGNED_INT_24_8_OES, 
+        component_type = GL_UNSIGNED_INT_24_8_OES;
 #else
 #else
-                        GL_UNSIGNED_INT_24_8_EXT, 
-#endif  // OPENGLES_1
-                        NULL);
-      } else {
-#endif  // OPENGLES_2
-        GLP(TexImage2D)(page_target, 0, internal_format,
-                        width, height, 0,
-                        external_format, GL_UNSIGNED_BYTE, NULL);
-#ifndef OPENGLES_2
+        component_type = GL_UNSIGNED_INT_24_8_EXT;
+#endif
+      }
+#endif
+
+      // We don't have any RAM mipmap levels, so we create an uninitialized OpenGL
+      // texture.  Presumably this will be used later for render-to-texture or so.
+      switch (page_target) {
+        case GL_TEXTURE_1D:
+          GLP(TexImage1D)(page_target, 0, internal_format, width, 0, external_format, component_type, NULL);
+          break;
+        case GL_TEXTURE_3D:
+        case GL_TEXTURE_2D_ARRAY:
+          _glTexImage3D(page_target, 0, internal_format, width, height, depth, 0, external_format, component_type, NULL);
+          break;
+        default:
+          GLP(TexImage2D)(page_target, 0, internal_format, width, height, 0, external_format, component_type, NULL);
+          break;
       }
       }
-#endif  // OPENGLES_2
     }
     }
-    
+
     for (int n = mipmap_bias; n < num_ram_mipmap_levels; ++n) {
     for (int n = mipmap_bias; n < num_ram_mipmap_levels; ++n) {
       const unsigned char *image_ptr = (unsigned char*)tex->get_ram_mipmap_pointer(n);
       const unsigned char *image_ptr = (unsigned char*)tex->get_ram_mipmap_pointer(n);
       CPTA_uchar ptimage;
       CPTA_uchar ptimage;
@@ -9283,7 +9356,7 @@ upload_texture_image(CLP(TextureContext) *gtc,
       GLCAT.error()
       GLCAT.error()
         << "GL texture creation failed for " << tex->get_name()
         << "GL texture creation failed for " << tex->get_name()
         << " : " << get_error_string(error_code) << "\n";
         << " : " << get_error_string(error_code) << "\n";
-      
+
       gtc->_already_applied = false;
       gtc->_already_applied = false;
       return false;
       return false;
     }
     }
@@ -9642,6 +9715,7 @@ do_extract_texture_data(CLP(TextureContext) *gtc) {
     break;
     break;
   case GL_DEPTH_COMPONENT24:
   case GL_DEPTH_COMPONENT24:
   case GL_DEPTH_COMPONENT32:
   case GL_DEPTH_COMPONENT32:
+  case GL_DEPTH_COMPONENT32F:
     type = Texture::T_float;
     type = Texture::T_float;
     format = Texture::F_depth_component;
     format = Texture::F_depth_component;
     break;
     break;
@@ -10034,7 +10108,7 @@ get_supports_cg_profile(const string &name) const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: GLGraphicsStateGuardian::bind_fbo
 //     Function: GLGraphicsStateGuardian::bind_fbo
 //       Access: Protected
 //       Access: Protected
-//  Description: Binds an FBO object.
+//  Description: Binds a framebuffer object.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void CLP(GraphicsStateGuardian)::
 void CLP(GraphicsStateGuardian)::
 bind_fbo(GLuint fbo) {
 bind_fbo(GLuint fbo) {

+ 6 - 0
panda/src/glstuff/glGraphicsStateGuardian_src.h

@@ -67,6 +67,7 @@ typedef void (APIENTRYP PFNGLDRAWRANGEELEMENTSPROC) (GLenum mode, GLuint start,
 // There is some trivial disagreement between different gl.h headers about this one, so we use our own typename.
 // There is some trivial disagreement between different gl.h headers about this one, so we use our own typename.
 typedef void (APIENTRYP PFNGLTEXIMAGE3DPROC_P) (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const GLvoid *pixels);
 typedef void (APIENTRYP PFNGLTEXIMAGE3DPROC_P) (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const GLvoid *pixels);
 typedef void (APIENTRYP PFNGLTEXSUBIMAGE3DPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const GLvoid *pixels);
 typedef void (APIENTRYP PFNGLTEXSUBIMAGE3DPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const GLvoid *pixels);
+typedef void (APIENTRYP PFNGLCOPYTEXSUBIMAGE3DPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height);
 typedef void (APIENTRYP PFNGLACTIVETEXTUREPROC) (GLenum texture);
 typedef void (APIENTRYP PFNGLACTIVETEXTUREPROC) (GLenum texture);
 typedef void (APIENTRYP PFNGLMULTITEXCOORD1FPROC) (GLenum target, const GLfloat s);
 typedef void (APIENTRYP PFNGLMULTITEXCOORD1FPROC) (GLenum target, const GLfloat s);
 typedef void (APIENTRYP PFNGLMULTITEXCOORD2FPROC) (GLenum target, const GLfloat s, const GLfloat t);
 typedef void (APIENTRYP PFNGLMULTITEXCOORD2FPROC) (GLenum target, const GLfloat s, const GLfloat t);
@@ -115,6 +116,8 @@ typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE3DOES) (GLenum target, GLenum att
 #else
 #else
 typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE3DEXTPROC) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset); 
 typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE3DEXTPROC) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset); 
 #endif
 #endif
+typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTUREARBPROC) (GLenum target, GLenum attachment, GLuint texture, GLint level);
+typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURELAYERPROC) (GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer);
 typedef void (APIENTRYP PFNGLFRAMEBUFFERRENDERBUFFEREXTPROC) (GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); 
 typedef void (APIENTRYP PFNGLFRAMEBUFFERRENDERBUFFEREXTPROC) (GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); 
 typedef void (APIENTRYP PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVEXTPROC) (GLenum target, GLenum attachment, GLenum pname, GLint *params); 
 typedef void (APIENTRYP PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVEXTPROC) (GLenum target, GLenum attachment, GLenum pname, GLint *params); 
 typedef void (APIENTRYP PFNGLGENERATEMIPMAPEXTPROC) (GLenum target);
 typedef void (APIENTRYP PFNGLGENERATEMIPMAPEXTPROC) (GLenum target);
@@ -556,6 +559,7 @@ public:
 
 
   PFNGLTEXIMAGE3DPROC_P _glTexImage3D;
   PFNGLTEXIMAGE3DPROC_P _glTexImage3D;
   PFNGLTEXSUBIMAGE3DPROC _glTexSubImage3D;
   PFNGLTEXSUBIMAGE3DPROC _glTexSubImage3D;
+  PFNGLCOPYTEXSUBIMAGE3DPROC _glCopyTexSubImage3D;
 
 
   PFNGLCOMPRESSEDTEXIMAGE1DPROC _glCompressedTexImage1D;
   PFNGLCOMPRESSEDTEXIMAGE1DPROC _glCompressedTexImage1D;
   PFNGLCOMPRESSEDTEXIMAGE2DPROC _glCompressedTexImage2D;
   PFNGLCOMPRESSEDTEXIMAGE2DPROC _glCompressedTexImage2D;
@@ -610,6 +614,8 @@ public:
 #else
 #else
   PFNGLFRAMEBUFFERTEXTURE3DEXTPROC _glFramebufferTexture3D;
   PFNGLFRAMEBUFFERTEXTURE3DEXTPROC _glFramebufferTexture3D;
 #endif
 #endif
+  PFNGLFRAMEBUFFERTEXTUREARBPROC _glFramebufferTexture;
+  PFNGLFRAMEBUFFERTEXTURELAYERPROC _glFramebufferTextureLayer;
   PFNGLFRAMEBUFFERRENDERBUFFEREXTPROC _glFramebufferRenderbuffer;
   PFNGLFRAMEBUFFERRENDERBUFFEREXTPROC _glFramebufferRenderbuffer;
   PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVEXTPROC _glGetFramebufferAttachmentParameteriv;
   PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVEXTPROC _glGetFramebufferAttachmentParameteriv;
   PFNGLGENERATEMIPMAPEXTPROC _glGenerateMipmap;
   PFNGLGENERATEMIPMAPEXTPROC _glGenerateMipmap;

+ 10 - 7
panda/src/glxdisplay/glxGraphicsPipe.cxx

@@ -118,7 +118,8 @@ make_output(const string &name,
         ((flags&BF_size_track_host)!=0)||
         ((flags&BF_size_track_host)!=0)||
         ((flags&BF_rtt_cumulative)!=0)||
         ((flags&BF_rtt_cumulative)!=0)||
         ((flags&BF_can_bind_color)!=0)||
         ((flags&BF_can_bind_color)!=0)||
-        ((flags&BF_can_bind_every)!=0)) {
+        ((flags&BF_can_bind_every)!=0)||
+        ((flags&BF_can_bind_layered)!=0)) {
       return NULL;
       return NULL;
     }
     }
     return new glxGraphicsWindow(engine, this, name, fb_prop, win_prop,
     return new glxGraphicsWindow(engine, this, name, fb_prop, win_prop,
@@ -168,14 +169,15 @@ make_output(const string &name,
       if (!glx_support_pbuffer) {
       if (!glx_support_pbuffer) {
         return NULL;
         return NULL;
       }
       }
-      
+
       if (((flags&BF_require_parasite)!=0)||
       if (((flags&BF_require_parasite)!=0)||
           ((flags&BF_require_window)!=0)||
           ((flags&BF_require_window)!=0)||
           ((flags&BF_resizeable)!=0)||
           ((flags&BF_resizeable)!=0)||
-          ((flags&BF_size_track_host)!=0)) {
+          ((flags&BF_size_track_host)!=0)||
+          ((flags&BF_can_bind_layered)!=0) {
         return NULL;
         return NULL;
       }
       }
-      
+
       if (!support_rtt) {
       if (!support_rtt) {
         if (((flags&BF_rtt_cumulative)!=0)||
         if (((flags&BF_rtt_cumulative)!=0)||
             ((flags&BF_can_bind_every)!=0)) {
             ((flags&BF_can_bind_every)!=0)) {
@@ -184,7 +186,7 @@ make_output(const string &name,
           return NULL;
           return NULL;
         }
         }
       }
       }
-      
+
       return new glxGraphicsBuffer(engine, this, name, fb_prop, win_prop,
       return new glxGraphicsBuffer(engine, this, name, fb_prop, win_prop,
                                    flags, gsg, host);
                                    flags, gsg, host);
     }
     }
@@ -199,7 +201,8 @@ make_output(const string &name,
     if (((flags&BF_require_parasite)!=0)||
     if (((flags&BF_require_parasite)!=0)||
         ((flags&BF_require_window)!=0)||
         ((flags&BF_require_window)!=0)||
         ((flags&BF_resizeable)!=0)||
         ((flags&BF_resizeable)!=0)||
-        ((flags&BF_size_track_host)!=0)) {
+        ((flags&BF_size_track_host)!=0)||
+        ((flags&BF_can_bind_layered)!=0) {
       return NULL;
       return NULL;
     }
     }
 
 
@@ -211,7 +214,7 @@ make_output(const string &name,
     return new glxGraphicsPixmap(engine, this, name, fb_prop, win_prop,
     return new glxGraphicsPixmap(engine, this, name, fb_prop, win_prop,
                                  flags, gsg, host);
                                  flags, gsg, host);
   }
   }
-  
+
   // Nothing else left to try.
   // Nothing else left to try.
   return NULL;
   return NULL;
 }
 }

+ 5 - 3
panda/src/osxdisplay/osxGraphicsPipe.cxx

@@ -392,7 +392,8 @@ make_output(const string &name,
         ((flags&BF_resizeable)!=0)||
         ((flags&BF_resizeable)!=0)||
         ((flags&BF_size_track_host)!=0)||
         ((flags&BF_size_track_host)!=0)||
         ((flags&BF_can_bind_color)!=0)||
         ((flags&BF_can_bind_color)!=0)||
-        ((flags&BF_can_bind_every)!=0)) {
+        ((flags&BF_can_bind_every)!=0)||
+        ((flags&BF_can_bind_layered)!=0)) {
       return NULL;
       return NULL;
     }
     }
     WindowHandle *window_handle = win_prop.get_parent_window();
     WindowHandle *window_handle = win_prop.get_parent_window();
@@ -412,7 +413,7 @@ make_output(const string &name,
                                  flags, gsg, host);
                                  flags, gsg, host);
   }
   }
   
   
-  // Second thing to try: a glGraphicsBuffer
+  // Second thing to try: a GLGraphicsBuffer
   
   
   if (retry == 1) {
   if (retry == 1) {
     if (!osx_support_gl_buffer) {
     if (!osx_support_gl_buffer) {
@@ -453,7 +454,8 @@ make_output(const string &name,
         ((flags&BF_require_window)!=0)||
         ((flags&BF_require_window)!=0)||
         ((flags&BF_resizeable)!=0)||
         ((flags&BF_resizeable)!=0)||
         ((flags&BF_size_track_host)!=0)||
         ((flags&BF_size_track_host)!=0)||
-        ((flags&BF_can_bind_every)!=0)) {
+        ((flags&BF_can_bind_every)!=0)||
+        ((flags&BF_can_bind_layered)!=0)) {
       return NULL;
       return NULL;
     }
     }
     return new osxGraphicsBuffer(engine, this, name, fb_prop, win_prop,
     return new osxGraphicsBuffer(engine, this, name, fb_prop, win_prop,

+ 4 - 2
panda/src/wgldisplay/wglGraphicsPipe.cxx

@@ -139,7 +139,8 @@ make_output(const string &name,
         ((flags&BF_size_track_host)!=0)||
         ((flags&BF_size_track_host)!=0)||
         ((flags&BF_rtt_cumulative)!=0)||
         ((flags&BF_rtt_cumulative)!=0)||
         ((flags&BF_can_bind_color)!=0)||
         ((flags&BF_can_bind_color)!=0)||
-        ((flags&BF_can_bind_every)!=0)) {
+        ((flags&BF_can_bind_every)!=0)||
+        ((flags&BF_can_bind_layered)!=0)) {
       return NULL;
       return NULL;
     }
     }
     if ((flags & BF_fb_props_optional)==0) {
     if ((flags & BF_fb_props_optional)==0) {
@@ -201,7 +202,8 @@ make_output(const string &name,
   
   
   if (retry == 2) {
   if (retry == 2) {
     if (((flags&BF_require_parasite)!=0)||
     if (((flags&BF_require_parasite)!=0)||
-        ((flags&BF_require_window)!=0)) {
+        ((flags&BF_require_window)!=0)||
+        ((flags&BF_can_bind_layered)!=0)) {
       return NULL;
       return NULL;
     }
     }
     if ((wglgsg != 0) &&
     if ((wglgsg != 0) &&