Browse Source

Add support for buffer textures

rdb 10 years ago
parent
commit
2ff9083812

+ 26 - 0
panda/src/display/graphicsStateGuardian.I

@@ -397,6 +397,22 @@ get_max_cube_map_dimension() const {
   return _max_cube_map_dimension;
   return _max_cube_map_dimension;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsStateGuardian::get_max_buffer_texture_size
+//       Access: Published
+//  Description: Returns the largest possible buffer texture size,
+//               or -1 if there is no particular limit.  Returns 0
+//               if cube map textures are not supported.
+//
+//               The value returned may not be meaningful until after
+//               the graphics context has been fully created (e.g. the
+//               window has been opened).
+////////////////////////////////////////////////////////////////////
+INLINE int GraphicsStateGuardian::
+get_max_buffer_texture_size() const {
+  return _max_buffer_texture_size;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: GraphicsStateGuardian::get_supports_texture_combine
 //     Function: GraphicsStateGuardian::get_supports_texture_combine
 //       Access: Published
 //       Access: Published
@@ -469,6 +485,16 @@ get_supports_cube_map() const {
   return _supports_cube_map;
   return _supports_cube_map;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsStateGuardian::get_supports_buffer_texture
+//       Access: Published
+//  Description: Returns true if this GSG can render buffer textures.
+////////////////////////////////////////////////////////////////////
+INLINE bool GraphicsStateGuardian::
+get_supports_buffer_texture() const {
+  return _supports_buffer_texture;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: GraphicsStateGuardian::get_supports_tex_non_pow2
 //     Function: GraphicsStateGuardian::get_supports_tex_non_pow2
 //       Access: Published
 //       Access: Published

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

@@ -185,6 +185,7 @@ GraphicsStateGuardian(CoordinateSystem internal_coordinate_system,
   _max_3d_texture_dimension = 0;
   _max_3d_texture_dimension = 0;
   _max_2d_texture_array_layers = 0;
   _max_2d_texture_array_layers = 0;
   _max_cube_map_dimension = 0;
   _max_cube_map_dimension = 0;
+  _max_buffer_texture_size = 0;
 
 
   // Assume we don't support these fairly advanced texture combiner
   // Assume we don't support these fairly advanced texture combiner
   // modes.
   // modes.
@@ -195,6 +196,7 @@ GraphicsStateGuardian(CoordinateSystem internal_coordinate_system,
   _supports_3d_texture = false;
   _supports_3d_texture = false;
   _supports_2d_texture_array = false;
   _supports_2d_texture_array = false;
   _supports_cube_map = false;
   _supports_cube_map = false;
+  _supports_buffer_texture = false;
   _supports_tex_non_pow2 = false;
   _supports_tex_non_pow2 = false;
   _supports_texture_srgb = false;
   _supports_texture_srgb = false;
   _supports_compressed_texture = false;
   _supports_compressed_texture = false;

+ 4 - 0
panda/src/display/graphicsStateGuardian.h

@@ -121,6 +121,7 @@ PUBLISHED:
   INLINE int get_max_3d_texture_dimension() const;
   INLINE int get_max_3d_texture_dimension() const;
   INLINE int get_max_2d_texture_array_layers() const; //z axis
   INLINE int get_max_2d_texture_array_layers() const; //z axis
   INLINE int get_max_cube_map_dimension() const;
   INLINE int get_max_cube_map_dimension() const;
+  INLINE int get_max_buffer_texture_size() const;
 
 
   INLINE bool get_supports_texture_combine() const;
   INLINE bool get_supports_texture_combine() const;
   INLINE bool get_supports_texture_saved_result() const;
   INLINE bool get_supports_texture_saved_result() const;
@@ -129,6 +130,7 @@ PUBLISHED:
   INLINE bool get_supports_3d_texture() const;
   INLINE bool get_supports_3d_texture() const;
   INLINE bool get_supports_2d_texture_array() const;
   INLINE bool get_supports_2d_texture_array() const;
   INLINE bool get_supports_cube_map() const;
   INLINE bool get_supports_cube_map() const;
+  INLINE bool get_supports_buffer_texture() const;
   INLINE bool get_supports_tex_non_pow2() const;
   INLINE bool get_supports_tex_non_pow2() const;
   INLINE bool get_supports_texture_srgb() const;
   INLINE bool get_supports_texture_srgb() const;
 
 
@@ -476,6 +478,7 @@ protected:
   int _max_3d_texture_dimension;
   int _max_3d_texture_dimension;
   int _max_2d_texture_array_layers; //on the z axis
   int _max_2d_texture_array_layers; //on the z axis
   int _max_cube_map_dimension;
   int _max_cube_map_dimension;
+  int _max_buffer_texture_size;
 
 
   bool _supports_texture_combine;
   bool _supports_texture_combine;
   bool _supports_texture_saved_result;
   bool _supports_texture_saved_result;
@@ -484,6 +487,7 @@ protected:
   bool _supports_3d_texture;
   bool _supports_3d_texture;
   bool _supports_2d_texture_array;
   bool _supports_2d_texture_array;
   bool _supports_cube_map;
   bool _supports_cube_map;
+  bool _supports_buffer_texture;
   bool _supports_tex_non_pow2;
   bool _supports_tex_non_pow2;
   bool _supports_texture_srgb;
   bool _supports_texture_srgb;
 
 

+ 132 - 23
panda/src/glstuff/glGraphicsStateGuardian_src.cxx

@@ -854,6 +854,17 @@ reset() {
   }
   }
 #endif
 #endif
 
 
+#ifndef OPENGLES
+  if (is_at_least_gl_version(3, 0)) {
+    _glTexBuffer = (PFNGLTEXBUFFERPROC)get_extension_func("glTexBuffer");
+    _supports_buffer_texture = true;
+
+  } else if (has_extension("GL_ARB_texture_buffer_object")) {
+    _glTexBuffer = (PFNGLTEXBUFFERPROC)get_extension_func("glTexBufferARB");
+    _supports_buffer_texture = true;
+  }
+#endif
+
   _supports_texture_srgb = false;
   _supports_texture_srgb = false;
   if (is_at_least_gl_version(2, 1) || has_extension("GL_EXT_texture_sRGB")) {
   if (is_at_least_gl_version(2, 1) || has_extension("GL_EXT_texture_sRGB")) {
     _supports_texture_srgb = true;
     _supports_texture_srgb = true;
@@ -1941,6 +1952,7 @@ reset() {
   GLint max_3d_texture_size = 0;
   GLint max_3d_texture_size = 0;
   GLint max_2d_texture_array_layers = 0;
   GLint max_2d_texture_array_layers = 0;
   GLint max_cube_map_size = 0;
   GLint max_cube_map_size = 0;
+  GLint max_buffer_texture_size = 0;
 
 
   glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_texture_size);
   glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_texture_size);
   _max_texture_dimension = max_texture_size;
   _max_texture_dimension = max_texture_size;
@@ -1966,6 +1978,15 @@ reset() {
     _max_cube_map_dimension = 0;
     _max_cube_map_dimension = 0;
   }
   }
 
 
+#ifndef OPENGLES
+  if (_supports_buffer_texture) {
+    glGetIntegerv(GL_MAX_TEXTURE_BUFFER_SIZE, &max_buffer_texture_size);
+    _max_buffer_texture_size = max_buffer_texture_size;
+  } else {
+    _max_buffer_texture_size = 0;
+  }
+#endif  // !OPENGLES
+
   GLint max_elements_vertices = 0, max_elements_indices = 0;
   GLint max_elements_vertices = 0, max_elements_indices = 0;
 #ifndef OPENGLES
 #ifndef OPENGLES
   glGetIntegerv(GL_MAX_ELEMENTS_VERTICES, &max_elements_vertices);
   glGetIntegerv(GL_MAX_ELEMENTS_VERTICES, &max_elements_vertices);
@@ -4510,6 +4531,15 @@ prepare_texture(Texture *tex, int view) {
         << "Cube map textures are not supported by this OpenGL driver.\n";
         << "Cube map textures are not supported by this OpenGL driver.\n";
       return NULL;
       return NULL;
     }
     }
+    break;
+
+  case Texture::TT_buffer_texture:
+    if (!_supports_buffer_texture) {
+      GLCAT.warning()
+        << "Buffer textures are not supported by this OpenGL driver.\n";
+      return NULL;
+    }
+    break;
 
 
   default:
   default:
     break;
     break;
@@ -7362,27 +7392,35 @@ get_texture_target(Texture::TextureType texture_type) const {
     return GL_TEXTURE_2D;
     return GL_TEXTURE_2D;
 
 
   case Texture::TT_3d_texture:
   case Texture::TT_3d_texture:
-    if (_supports_3d_texture) {
 #ifndef OPENGLES_1
 #ifndef OPENGLES_1
+    if (_supports_3d_texture) {
       return GL_TEXTURE_3D;
       return GL_TEXTURE_3D;
-#endif
-    } else {
-      return GL_NONE;
     }
     }
+#endif
+    return GL_NONE;
+
   case Texture::TT_2d_texture_array:
   case Texture::TT_2d_texture_array:
-    if (_supports_2d_texture_array) {
 #ifndef OPENGLES
 #ifndef OPENGLES
+    if (_supports_2d_texture_array) {
       return GL_TEXTURE_2D_ARRAY_EXT;
       return GL_TEXTURE_2D_ARRAY_EXT;
-#endif
-    } else {
-      return GL_NONE;
     }
     }
+#endif
+    return GL_NONE;
+
   case Texture::TT_cube_map:
   case Texture::TT_cube_map:
     if (_supports_cube_map) {
     if (_supports_cube_map) {
       return GL_TEXTURE_CUBE_MAP;
       return GL_TEXTURE_CUBE_MAP;
     } else {
     } else {
       return GL_NONE;
       return GL_NONE;
     }
     }
+
+  case Texture::TT_buffer_texture:
+#ifndef OPENGLES
+    if (_supports_buffer_texture) {
+      return GL_TEXTURE_BUFFER;
+    }
+#endif
+    return GL_NONE;
   }
   }
 
 
   GLCAT.error() << "Invalid Texture::TextureType value!\n";
   GLCAT.error() << "Invalid Texture::TextureType value!\n";
@@ -10252,6 +10290,12 @@ specify_texture(CLP(TextureContext) *gtc, const SamplerState &sampler) {
     // Unsupported target (e.g. 3-d texturing on GL 1.1).
     // Unsupported target (e.g. 3-d texturing on GL 1.1).
     return false;
     return false;
   }
   }
+#ifndef OPENGLES
+  if (target == GL_TEXTURE_BUFFER) {
+    // Buffer textures may not receive texture parameters.
+    return false;
+  }
+#endif  // OPENGLES
 
 
   // Record the active sampler settings.
   // Record the active sampler settings.
   gtc->_active_sampler = sampler;
   gtc->_active_sampler = sampler;
@@ -10505,7 +10549,8 @@ upload_texture(CLP(TextureContext) *gtc, bool force, bool uses_mipmaps) {
 
 
   // If we'll use immutable texture storage, we have to pick a sized
   // If we'll use immutable texture storage, we have to pick a sized
   // image format.
   // image format.
-  bool force_sized = (gl_immutable_texture_storage && _supports_tex_storage);
+  bool force_sized = (gl_immutable_texture_storage && _supports_tex_storage) ||
+                     (tex->get_texture_type() == Texture::TT_buffer_texture);
 
 
   GLint internal_format = get_internal_image_format(tex, force_sized);
   GLint internal_format = get_internal_image_format(tex, force_sized);
   GLint external_format = get_external_image_format(tex);
   GLint external_format = get_external_image_format(tex);
@@ -10553,6 +10598,12 @@ upload_texture(CLP(TextureContext) *gtc, bool force, bool uses_mipmaps) {
     max_dimension_z = _max_2d_texture_array_layers;
     max_dimension_z = _max_2d_texture_array_layers;
     break;
     break;
 
 
+  case Texture::TT_buffer_texture:
+    max_dimension_x = _max_buffer_texture_size;
+    max_dimension_y = 1;
+    max_dimension_z = 1;
+    break;
+
   default:
   default:
     max_dimension_x = _max_texture_dimension;
     max_dimension_x = _max_texture_dimension;
     max_dimension_y = _max_texture_dimension;
     max_dimension_y = _max_texture_dimension;
@@ -10631,6 +10682,13 @@ upload_texture(CLP(TextureContext) *gtc, bool force, bool uses_mipmaps) {
 
 
   GLenum target = get_texture_target(tex->get_texture_type());
   GLenum target = get_texture_target(tex->get_texture_type());
   uses_mipmaps = (uses_mipmaps && !gl_ignore_mipmaps) || gl_force_mipmaps;
   uses_mipmaps = (uses_mipmaps && !gl_ignore_mipmaps) || gl_force_mipmaps;
+#ifndef OPENGLES
+  if (target == GL_TEXTURE_BUFFER) {
+    // Buffer textures may not have mipmaps.
+    uses_mipmaps = false;
+  }
+#endif  // OPENGLES
+
   bool needs_reload = false;
   bool needs_reload = false;
   if (!gtc->_has_storage ||
   if (!gtc->_has_storage ||
       gtc->_uses_mipmaps != uses_mipmaps ||
       gtc->_uses_mipmaps != uses_mipmaps ||
@@ -10640,6 +10698,12 @@ upload_texture(CLP(TextureContext) *gtc, bool force, bool uses_mipmaps) {
       gtc->_depth != depth) {
       gtc->_depth != depth) {
     // We need to reload a new GL Texture object.
     // We need to reload a new GL Texture object.
     needs_reload = true;
     needs_reload = true;
+
+    if (_use_object_labels) {
+      // This seems like a good time to assign a label for the debug messages.
+      const string &name = tex->get_name();
+      _glObjectLabel(GL_TEXTURE, gtc->_index, name.size(), name.data());
+    }
   }
   }
 
 
   if (needs_reload && gtc->_immutable) {
   if (needs_reload && gtc->_immutable) {
@@ -10648,13 +10712,26 @@ upload_texture(CLP(TextureContext) *gtc, bool force, bool uses_mipmaps) {
     glBindTexture(target, gtc->_index);
     glBindTexture(target, gtc->_index);
   }
   }
 
 
-  if (needs_reload) {
-    if (_use_object_labels) {
-      // This seems like a good time to assign a label for the debug messages.
-      const string &name = tex->get_name();
-      _glObjectLabel(GL_TEXTURE, gtc->_index, name.size(), name.data());
-    }
+#ifndef OPENGLES
+  if (target == GL_TEXTURE_BUFFER) {
+    // Buffer textures don't support mipmappping.
+    gtc->_generate_mipmaps = false;
 
 
+    if (gtc->_buffer == 0) {
+      // The buffer object wasn't created yet.
+      _glGenBuffers(1, &gtc->_buffer);
+      _glBindBuffer(GL_TEXTURE_BUFFER, gtc->_buffer);
+      _glTexBuffer(GL_TEXTURE_BUFFER, internal_format, gtc->_buffer);
+      needs_reload = true;
+    } else {
+      _glBindBuffer(GL_TEXTURE_BUFFER, gtc->_buffer);
+      if (gtc->_internal_format != internal_format) {
+        _glTexBuffer(GL_TEXTURE_BUFFER, internal_format, gtc->_buffer);
+      }
+    }
+  } else
+#endif  // !OPENGLES
+  if (needs_reload) {
     // Figure out whether mipmaps will be generated by the GPU or by
     // Figure out whether mipmaps will be generated by the GPU or by
     // Panda (or not at all), and how many mipmap levels should be created.
     // Panda (or not at all), and how many mipmap levels should be created.
     gtc->_generate_mipmaps = false;
     gtc->_generate_mipmaps = false;
@@ -10736,6 +10813,8 @@ upload_texture(CLP(TextureContext) *gtc, bool force, bool uses_mipmaps) {
       }
       }
 
 
       switch (tex->get_texture_type()) {
       switch (tex->get_texture_type()) {
+      case Texture::TT_buffer_texture:
+        // Won't get here, but squelch compiler warning
       case Texture::TT_1d_texture:
       case Texture::TT_1d_texture:
         _glTexStorage1D(target, num_levels, internal_format, width);
         _glTexStorage1D(target, num_levels, internal_format, width);
         break;
         break;
@@ -11095,6 +11174,17 @@ upload_texture_image(CLP(TextureContext) *gtc, bool needs_reload,
         break;
         break;
 #endif  // OPENGLES
 #endif  // OPENGLES
 
 
+#ifndef OPENGLES
+      case GL_TEXTURE_BUFFER:
+        if (_supports_buffer_texture) {
+          _glBufferSubData(GL_TEXTURE_BUFFER, 0, width, image_ptr);
+        } else {
+          report_my_gl_errors();
+          return false;
+        }
+        break;
+#endif  // OPENGLES
+
       default:
       default:
         if (image_compression == Texture::CM_off) {
         if (image_compression == Texture::CM_off) {
           if (n==0) {
           if (n==0) {
@@ -11248,6 +11338,7 @@ upload_texture_image(CLP(TextureContext) *gtc, bool needs_reload,
         }
         }
         break;
         break;
 #endif
 #endif
+
 #ifndef OPENGLES
 #ifndef OPENGLES
       case GL_TEXTURE_2D_ARRAY_EXT:
       case GL_TEXTURE_2D_ARRAY_EXT:
         if (_supports_2d_texture_array) {
         if (_supports_2d_texture_array) {
@@ -11265,7 +11356,19 @@ upload_texture_image(CLP(TextureContext) *gtc, bool needs_reload,
           return false;
           return false;
         }
         }
         break;
         break;
-#endif
+#endif  // OPENGLES
+
+#ifndef OPENGLES
+      case GL_TEXTURE_BUFFER:
+        if (_supports_buffer_texture) {
+          _glBufferData(GL_TEXTURE_BUFFER, width, image_ptr,
+                        get_usage(tex->get_usage_hint()));
+        } else {
+          report_my_gl_errors();
+          return false;
+        }
+        break;
+#endif  // OPENGLES
 
 
       default:
       default:
         if (image_compression == Texture::CM_off) {
         if (image_compression == Texture::CM_off) {
@@ -11396,6 +11499,12 @@ get_texture_memory_size(Texture *tex) {
     // We need a particular page to get the level parameter from.
     // We need a particular page to get the level parameter from.
     page_target = GL_TEXTURE_CUBE_MAP_POSITIVE_X;
     page_target = GL_TEXTURE_CUBE_MAP_POSITIVE_X;
     scale = 6;
     scale = 6;
+
+  } else if (target == GL_TEXTURE_BUFFER) {
+    // In the case of buffer textures, we provided the size to begin with,
+    // so no point in querying anything.  Plus, glGetTexParameter is not even
+    // supported for buffer textures.
+    return tex->get_expected_ram_image_size();
   }
   }
 
 
   GLint minfilter;
   GLint minfilter;
@@ -11431,20 +11540,20 @@ get_texture_memory_size(Texture *tex) {
     luminance_size, intensity_size;
     luminance_size, intensity_size;
   GLint depth_size = 0;
   GLint depth_size = 0;
   glGetTexLevelParameteriv(page_target, 0,
   glGetTexLevelParameteriv(page_target, 0,
-                              GL_TEXTURE_RED_SIZE, &red_size);
+                           GL_TEXTURE_RED_SIZE, &red_size);
   glGetTexLevelParameteriv(page_target, 0,
   glGetTexLevelParameteriv(page_target, 0,
-                              GL_TEXTURE_GREEN_SIZE, &green_size);
+                           GL_TEXTURE_GREEN_SIZE, &green_size);
   glGetTexLevelParameteriv(page_target, 0,
   glGetTexLevelParameteriv(page_target, 0,
-                              GL_TEXTURE_BLUE_SIZE, &blue_size);
+                           GL_TEXTURE_BLUE_SIZE, &blue_size);
   glGetTexLevelParameteriv(page_target, 0,
   glGetTexLevelParameteriv(page_target, 0,
-                              GL_TEXTURE_ALPHA_SIZE, &alpha_size);
+                           GL_TEXTURE_ALPHA_SIZE, &alpha_size);
   glGetTexLevelParameteriv(page_target, 0,
   glGetTexLevelParameteriv(page_target, 0,
-                              GL_TEXTURE_LUMINANCE_SIZE, &luminance_size);
+                           GL_TEXTURE_LUMINANCE_SIZE, &luminance_size);
   glGetTexLevelParameteriv(page_target, 0,
   glGetTexLevelParameteriv(page_target, 0,
-                              GL_TEXTURE_INTENSITY_SIZE, &intensity_size);
+                           GL_TEXTURE_INTENSITY_SIZE, &intensity_size);
   if (_supports_depth_texture) {
   if (_supports_depth_texture) {
     glGetTexLevelParameteriv(page_target, 0,
     glGetTexLevelParameteriv(page_target, 0,
-                                GL_TEXTURE_DEPTH_SIZE, &depth_size);
+                             GL_TEXTURE_DEPTH_SIZE, &depth_size);
   }
   }
 
 
   GLint width = 1, height = 1, depth = 1;
   GLint width = 1, height = 1, depth = 1;

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

@@ -693,6 +693,10 @@ public:
   PFNGLTEXSTORAGE2DPROC _glTexStorage2D;
   PFNGLTEXSTORAGE2DPROC _glTexStorage2D;
   PFNGLTEXSTORAGE3DPROC _glTexStorage3D;
   PFNGLTEXSTORAGE3DPROC _glTexStorage3D;
 
 
+#ifndef OPENGLES
+  PFNGLTEXBUFFERPROC _glTexBuffer;
+#endif
+
   bool _supports_clear_texture;
   bool _supports_clear_texture;
 #ifndef OPENGLES
 #ifndef OPENGLES
   PFNGLCLEARTEXIMAGEPROC _glClearTexImage;
   PFNGLCLEARTEXIMAGEPROC _glClearTexImage;

+ 25 - 1
panda/src/glstuff/glShaderContext_src.cxx

@@ -602,16 +602,19 @@ CLP(ShaderContext)(CLP(GraphicsStateGuardian) *glgsg, Shader *s) : ShaderContext
             case GL_INT_SAMPLER_3D:
             case GL_INT_SAMPLER_3D:
             case GL_INT_SAMPLER_2D_ARRAY:
             case GL_INT_SAMPLER_2D_ARRAY:
             case GL_INT_SAMPLER_CUBE:
             case GL_INT_SAMPLER_CUBE:
+            case GL_INT_SAMPLER_BUFFER:
             case GL_UNSIGNED_INT_SAMPLER_1D:
             case GL_UNSIGNED_INT_SAMPLER_1D:
             case GL_UNSIGNED_INT_SAMPLER_2D:
             case GL_UNSIGNED_INT_SAMPLER_2D:
             case GL_UNSIGNED_INT_SAMPLER_3D:
             case GL_UNSIGNED_INT_SAMPLER_3D:
             case GL_UNSIGNED_INT_SAMPLER_CUBE:
             case GL_UNSIGNED_INT_SAMPLER_CUBE:
             case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY:
             case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY:
+            case GL_UNSIGNED_INT_SAMPLER_BUFFER:
             case GL_SAMPLER_1D_SHADOW:
             case GL_SAMPLER_1D_SHADOW:
             case GL_SAMPLER_1D:
             case GL_SAMPLER_1D:
             case GL_SAMPLER_CUBE_SHADOW:
             case GL_SAMPLER_CUBE_SHADOW:
             case GL_SAMPLER_2D_ARRAY:
             case GL_SAMPLER_2D_ARRAY:
             case GL_SAMPLER_2D_ARRAY_SHADOW:
             case GL_SAMPLER_2D_ARRAY_SHADOW:
+            case GL_SAMPLER_BUFFER:
 #endif  // !OPENGLES
 #endif  // !OPENGLES
             case GL_SAMPLER_2D:
             case GL_SAMPLER_2D:
             case GL_SAMPLER_2D_SHADOW:
             case GL_SAMPLER_2D_SHADOW:
@@ -620,7 +623,7 @@ CLP(ShaderContext)(CLP(GraphicsStateGuardian) *glgsg, Shader *s) : ShaderContext
               Shader::ShaderTexSpec bind;
               Shader::ShaderTexSpec bind;
               bind._id = arg_id;
               bind._id = arg_id;
               bind._name = InternalName::make(param_name);
               bind._name = InternalName::make(param_name);
-              bind._desired_type = Texture::TT_2d_texture_array;
+              bind._desired_type = Texture::TT_2d_texture;
               bind._stage = texunitno++;
               bind._stage = texunitno++;
               if (get_sampler_texture_type(bind._desired_type, param_type)) {
               if (get_sampler_texture_type(bind._desired_type, param_type)) {
                 _glgsg->_glUniform1i(p, s->_tex_spec.size());
                 _glgsg->_glUniform1i(p, s->_tex_spec.size());
@@ -761,16 +764,19 @@ CLP(ShaderContext)(CLP(GraphicsStateGuardian) *glgsg, Shader *s) : ShaderContext
             case GL_IMAGE_3D_EXT:
             case GL_IMAGE_3D_EXT:
             case GL_IMAGE_CUBE_EXT:
             case GL_IMAGE_CUBE_EXT:
             case GL_IMAGE_2D_ARRAY_EXT:
             case GL_IMAGE_2D_ARRAY_EXT:
+            case GL_IMAGE_BUFFER_EXT:
             case GL_INT_IMAGE_1D_EXT:
             case GL_INT_IMAGE_1D_EXT:
             case GL_INT_IMAGE_2D_EXT:
             case GL_INT_IMAGE_2D_EXT:
             case GL_INT_IMAGE_3D_EXT:
             case GL_INT_IMAGE_3D_EXT:
             case GL_INT_IMAGE_CUBE_EXT:
             case GL_INT_IMAGE_CUBE_EXT:
             case GL_INT_IMAGE_2D_ARRAY_EXT:
             case GL_INT_IMAGE_2D_ARRAY_EXT:
+            case GL_INT_IMAGE_BUFFER_EXT:
             case GL_UNSIGNED_INT_IMAGE_1D_EXT:
             case GL_UNSIGNED_INT_IMAGE_1D_EXT:
             case GL_UNSIGNED_INT_IMAGE_2D_EXT:
             case GL_UNSIGNED_INT_IMAGE_2D_EXT:
             case GL_UNSIGNED_INT_IMAGE_3D_EXT:
             case GL_UNSIGNED_INT_IMAGE_3D_EXT:
             case GL_UNSIGNED_INT_IMAGE_CUBE_EXT:
             case GL_UNSIGNED_INT_IMAGE_CUBE_EXT:
             case GL_UNSIGNED_INT_IMAGE_2D_ARRAY_EXT:
             case GL_UNSIGNED_INT_IMAGE_2D_ARRAY_EXT:
+            case GL_UNSIGNED_INT_IMAGE_BUFFER_EXT:
               // This won't really change at runtime, so we might as well
               // This won't really change at runtime, so we might as well
               // bind once and then forget about it.
               // bind once and then forget about it.
               _glgsg->_glUniform1i(p, imgunitno++);
               _glgsg->_glUniform1i(p, imgunitno++);
@@ -1076,6 +1082,18 @@ get_sampler_texture_type(int &out, GLenum param_type) {
         << "GLSL shader uses 2D texture array, which is unsupported by the driver.\n";
         << "GLSL shader uses 2D texture array, which is unsupported by the driver.\n";
       return false;
       return false;
     }
     }
+
+  case GL_INT_SAMPLER_BUFFER:
+  case GL_UNSIGNED_INT_SAMPLER_BUFFER:
+  case GL_SAMPLER_BUFFER:
+    out = Texture::TT_buffer_texture;
+    if (_glgsg->_supports_buffer_texture) {
+      return true;
+    } else {
+      GLCAT.error()
+        << "GLSL shader uses buffer texture, which is unsupported by the driver.\n";
+      return false;
+    }
 #endif
 #endif
 
 
   default:
   default:
@@ -1509,6 +1527,12 @@ disable_shader_texture_bindings() {
     case Texture::TT_cube_map:
     case Texture::TT_cube_map:
       glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
       glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
       break;
       break;
+
+    case Texture::TT_buffer_texture:
+#ifndef OPENGLES
+      glBindTexture(GL_TEXTURE_BUFFER, 0);
+#endif
+      break;
     }
     }
   }
   }
 
 

+ 1 - 0
panda/src/glstuff/glTextureContext_src.I

@@ -26,6 +26,7 @@ CLP(TextureContext)(CLP(GraphicsStateGuardian) *glgsg,
   _glgsg = glgsg;
   _glgsg = glgsg;
 
 
   glGenTextures(1, &_index);
   glGenTextures(1, &_index);
+  _buffer = 0;
 
 
   _handle = 0;
   _handle = 0;
   _has_storage = false;
   _has_storage = false;

+ 10 - 0
panda/src/glstuff/glTextureContext_src.cxx

@@ -34,6 +34,11 @@ CLP(TextureContext)::
 
 
   glDeleteTextures(1, &_index);
   glDeleteTextures(1, &_index);
   _index = 0;
   _index = 0;
+
+  if (_buffer != 0) {
+    _glgsg->_glDeleteBuffers(1, &_buffer);
+    _buffer = 0;
+  }
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -88,6 +93,11 @@ reset_data() {
   // Free the texture resources.
   // Free the texture resources.
   glDeleteTextures(1, &_index);
   glDeleteTextures(1, &_index);
 
 
+  if (_buffer != 0) {
+    _glgsg->_glDeleteBuffers(1, &_buffer);
+    _buffer = 0;
+  }
+
   // We still need a valid index number, though, in case we want to
   // We still need a valid index number, though, in case we want to
   // re-load the texture later.
   // re-load the texture later.
   glGenTextures(1, &_index);
   glGenTextures(1, &_index);

+ 3 - 0
panda/src/glstuff/glTextureContext_src.h

@@ -48,6 +48,9 @@ public:
   // This is the GL "name" of the texture object.
   // This is the GL "name" of the texture object.
   GLuint _index;
   GLuint _index;
 
 
+  // This is only used for buffer textures.
+  GLuint _buffer;
+
   // This is the bindless "handle" to the texture object.
   // This is the bindless "handle" to the texture object.
   GLuint64 _handle;
   GLuint64 _handle;
   bool _handle_resident;
   bool _handle_resident;

+ 32 - 0
panda/src/gobj/texture.I

@@ -215,6 +215,26 @@ setup_cube_map(int size, ComponentType component_type, Format format) {
   setup_texture(TT_cube_map, size, size, 6, component_type, format);
   setup_texture(TT_cube_map, size, size, 6, component_type, format);
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: Texture::setup_buffer_texture
+//       Access: Published
+//  Description: Sets the texture as an empty buffer texture with
+//               the specified size and properties.  Follow up
+//               with set_ram_image() or modify_ram_image() to fill
+//               the image data, or use set_clear_color to let the
+//               texture be cleared to a solid color.
+//
+//               Note that a buffer texture's format needs to match
+//               the component type.
+////////////////////////////////////////////////////////////////////
+INLINE void Texture::
+setup_buffer_texture(int size, ComponentType component_type, Format format,
+                     GeomEnums::UsageHint usage) {
+  setup_texture(TT_buffer_texture, size, 1, 1, component_type, format);
+  CDWriter cdata(_cycler);
+  cdata->_usage_hint = usage;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: Texture::clear_image
 //     Function: Texture::clear_image
 //       Access: Published
 //       Access: Published
@@ -886,6 +906,18 @@ get_component_type() const {
   return cdata->_component_type;
   return cdata->_component_type;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: Texture::get_usage_hint
+//       Access: Published
+//  Description: Returns the usage hint specified for buffer textures,
+//               or UH_unspecified for all other texture types.
+////////////////////////////////////////////////////////////////////
+INLINE GeomEnums::UsageHint Texture::
+get_usage_hint() const {
+  CDReader cdata(_cycler);
+  return cdata->_usage_hint;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: Texture::set_wrap_u
 //     Function: Texture::set_wrap_u
 //       Access: Published
 //       Access: Published

+ 29 - 2
panda/src/gobj/texture.cxx

@@ -1524,6 +1524,10 @@ write(ostream &out, int indent_level) const {
   case TT_cube_map:
   case TT_cube_map:
     out << "cube map, " << cdata->_x_size << " x " << cdata->_y_size;
     out << "cube map, " << cdata->_x_size << " x " << cdata->_y_size;
     break;
     break;
+
+  case TT_buffer_texture:
+    out << "buffer, " << cdata->_x_size;
+    break;
   }
   }
 
 
   if (cdata->_num_views > 1) {
   if (cdata->_num_views > 1) {
@@ -1924,6 +1928,8 @@ format_texture_type(TextureType tt) {
     return "2d_texture_array";
     return "2d_texture_array";
   case TT_cube_map:
   case TT_cube_map:
     return "cube_map";
     return "cube_map";
+  case TT_buffer_texture:
+    return "buffer_texture";
   }
   }
   return "**invalid**";
   return "**invalid**";
 }
 }
@@ -1946,6 +1952,8 @@ string_texture_type(const string &str) {
     return TT_2d_texture_array;
     return TT_2d_texture_array;
   } else if (cmp_nocase(str, "cube_map") == 0) {
   } else if (cmp_nocase(str, "cube_map") == 0) {
     return TT_cube_map;
     return TT_cube_map;
+  } else if (cmp_nocase(str, "buffer_texture") == 0) {
+    return TT_buffer_texture;
   }
   }
 
 
   gobj_cat->error()
   gobj_cat->error()
@@ -2673,6 +2681,7 @@ do_read(CData *cdata, const Filename &fullpath, const Filename &alpha_fullpath,
     switch (cdata->_texture_type) {
     switch (cdata->_texture_type) {
     case TT_1d_texture:
     case TT_1d_texture:
     case TT_2d_texture:
     case TT_2d_texture:
+    case TT_buffer_texture:
       z_size = 1;
       z_size = 1;
       break;
       break;
 
 
@@ -4854,7 +4863,8 @@ do_reconsider_image_properties(CData *cdata, int x_size, int y_size, int num_com
     }
     }
 
 
 #ifndef NDEBUG
 #ifndef NDEBUG
-    if (cdata->_texture_type == TT_1d_texture) {
+    if (cdata->_texture_type == TT_1d_texture ||
+        cdata->_texture_type == TT_buffer_texture) {
       nassertr(y_size == 1, false);
       nassertr(y_size == 1, false);
     } else if (cdata->_texture_type == TT_cube_map) {
     } else if (cdata->_texture_type == TT_cube_map) {
       nassertr(x_size == y_size, false);
       nassertr(x_size == y_size, false);
@@ -5051,6 +5061,10 @@ do_setup_texture(CData *cdata, Texture::TextureType texture_type,
     cdata->_default_sampler.set_wrap_v(SamplerState::WM_clamp);
     cdata->_default_sampler.set_wrap_v(SamplerState::WM_clamp);
     cdata->_default_sampler.set_wrap_w(SamplerState::WM_clamp);
     cdata->_default_sampler.set_wrap_w(SamplerState::WM_clamp);
     break;
     break;
+
+  case TT_buffer_texture:
+    nassertv(y_size == 1 && z_size == 1);
+    break;
   }
   }
 
 
   if (texture_type != TT_2d_texture) {
   if (texture_type != TT_2d_texture) {
@@ -5199,7 +5213,8 @@ do_set_x_size(CData *cdata, int x_size) {
 void Texture::
 void Texture::
 do_set_y_size(CData *cdata, int y_size) {
 do_set_y_size(CData *cdata, int y_size) {
   if (cdata->_y_size != y_size) {
   if (cdata->_y_size != y_size) {
-    nassertv(cdata->_texture_type != Texture::TT_1d_texture || y_size == 1);
+    nassertv((cdata->_texture_type != Texture::TT_buffer_texture &&
+              cdata->_texture_type != Texture::TT_1d_texture) || y_size == 1);
     cdata->_y_size = y_size;
     cdata->_y_size = y_size;
     cdata->inc_image_modified();
     cdata->inc_image_modified();
     do_clear_ram_image(cdata);
     do_clear_ram_image(cdata);
@@ -7897,6 +7912,10 @@ do_write_datagram_body(CData *cdata, BamWriter *manager, Datagram &me) {
   me.add_uint8(cdata->_format);
   me.add_uint8(cdata->_format);
   me.add_uint8(cdata->_num_components);
   me.add_uint8(cdata->_num_components);
 
 
+  if (cdata->_texture_type == TT_buffer_texture) {
+    me.add_uint8(cdata->_usage_hint);
+  }
+
   me.add_uint8(cdata->_auto_texture_scale);
   me.add_uint8(cdata->_auto_texture_scale);
   me.add_uint32(cdata->_orig_file_x_size);
   me.add_uint32(cdata->_orig_file_x_size);
   me.add_uint32(cdata->_orig_file_y_size);
   me.add_uint32(cdata->_orig_file_y_size);
@@ -8063,6 +8082,7 @@ make_this_from_bam(const FactoryParams &params) {
       options.set_auto_texture_scale(auto_texture_scale);
       options.set_auto_texture_scale(auto_texture_scale);
 
 
       switch (texture_type) {
       switch (texture_type) {
+      case TT_buffer_texture:
       case TT_1d_texture:
       case TT_1d_texture:
       case TT_2d_texture:
       case TT_2d_texture:
         if (alpha_filename.empty()) {
         if (alpha_filename.empty()) {
@@ -8124,6 +8144,10 @@ do_fillin_body(CData *cdata, DatagramIterator &scan, BamReader *manager) {
   cdata->_format = (Format)scan.get_uint8();
   cdata->_format = (Format)scan.get_uint8();
   cdata->_num_components = scan.get_uint8();
   cdata->_num_components = scan.get_uint8();
 
 
+  if (cdata->_texture_type == TT_buffer_texture) {
+    cdata->_usage_hint = (GeomEnums::UsageHint)scan.get_uint8();
+  }
+
   cdata->inc_properties_modified();
   cdata->inc_properties_modified();
 
 
   cdata->_auto_texture_scale = ATS_unspecified;
   cdata->_auto_texture_scale = ATS_unspecified;
@@ -8304,6 +8328,9 @@ CData() {
   // check in do_set_format depending on an uninitialized value.
   // check in do_set_format depending on an uninitialized value.
   _format = F_rgba;
   _format = F_rgba;
 
 
+  // Only used for buffer textures.
+  _usage_hint = GeomEnums::UH_unspecified;
+
   _pad_x_size = 0;
   _pad_x_size = 0;
   _pad_y_size = 0;
   _pad_y_size = 0;
   _pad_z_size = 0;
   _pad_z_size = 0;

+ 6 - 0
panda/src/gobj/texture.h

@@ -42,6 +42,7 @@
 #include "samplerState.h"
 #include "samplerState.h"
 #include "pnmImage.h"
 #include "pnmImage.h"
 #include "colorSpace.h"
 #include "colorSpace.h"
+#include "geomEnums.h"
 
 
 class PNMImage;
 class PNMImage;
 class PfmFile;
 class PfmFile;
@@ -82,6 +83,7 @@ PUBLISHED:
     TT_3d_texture,
     TT_3d_texture,
     TT_2d_texture_array,
     TT_2d_texture_array,
     TT_cube_map,
     TT_cube_map,
+    TT_buffer_texture,
   };
   };
 
 
   enum ComponentType {
   enum ComponentType {
@@ -240,6 +242,8 @@ PUBLISHED:
   INLINE void setup_2d_texture_array(int z_size = 1);
   INLINE void setup_2d_texture_array(int z_size = 1);
   INLINE void setup_2d_texture_array(int x_size, int y_size, int z_size,
   INLINE void setup_2d_texture_array(int x_size, int y_size, int z_size,
                                      ComponentType component_type, Format format);
                                      ComponentType component_type, Format format);
+  INLINE void setup_buffer_texture(int size, ComponentType component_type,
+                                   Format format, GeomEnums::UsageHint usage);
   void generate_normalization_cube_map(int size);
   void generate_normalization_cube_map(int size);
   void generate_alpha_scale_map();
   void generate_alpha_scale_map();
 
 
@@ -304,6 +308,7 @@ PUBLISHED:
   INLINE TextureType get_texture_type() const;
   INLINE TextureType get_texture_type() const;
   INLINE Format get_format() const;
   INLINE Format get_format() const;
   INLINE ComponentType get_component_type() const;
   INLINE ComponentType get_component_type() const;
+  INLINE GeomEnums::UsageHint get_usage_hint() const;
 
 
   INLINE void set_wrap_u(WrapMode wrap);
   INLINE void set_wrap_u(WrapMode wrap);
   INLINE void set_wrap_v(WrapMode wrap);
   INLINE void set_wrap_v(WrapMode wrap);
@@ -829,6 +834,7 @@ protected:
     TextureType _texture_type;
     TextureType _texture_type;
     Format _format;
     Format _format;
     ComponentType _component_type;
     ComponentType _component_type;
+    GeomEnums::UsageHint _usage_hint;
 
 
     bool _loaded_from_image;
     bool _loaded_from_image;
     bool _loaded_from_txo;
     bool _loaded_from_txo;