Browse Source

Implement support for SSBOs

rdb 9 years ago
parent
commit
34068dc0c1

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

@@ -62,6 +62,14 @@ release_all_index_buffers() {
   return _prepared_objects->release_all_index_buffers();
   return _prepared_objects->release_all_index_buffers();
 }
 }
 
 
+/**
+ * Frees the resources for all index buffers associated with this GSG.
+ */
+INLINE int GraphicsStateGuardian::
+release_all_shader_buffers() {
+  return _prepared_objects->release_all_shader_buffers();
+}
+
 /**
 /**
  * Sets the active flag associated with the GraphicsStateGuardian.  If the
  * Sets the active flag associated with the GraphicsStateGuardian.  If the
  * GraphicsStateGuardian is marked inactive, nothing is rendered.  This is not
  * GraphicsStateGuardian is marked inactive, nothing is rendered.  This is not

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

@@ -62,12 +62,15 @@
 #include <algorithm>
 #include <algorithm>
 #include <limits.h>
 #include <limits.h>
 
 
-PStatCollector GraphicsStateGuardian::_vertex_buffer_switch_pcollector("Vertex buffer switch:Vertex");
-PStatCollector GraphicsStateGuardian::_index_buffer_switch_pcollector("Vertex buffer switch:Index");
+PStatCollector GraphicsStateGuardian::_vertex_buffer_switch_pcollector("Buffer switch:Vertex");
+PStatCollector GraphicsStateGuardian::_index_buffer_switch_pcollector("Buffer switch:Index");
+PStatCollector GraphicsStateGuardian::_shader_buffer_switch_pcollector("Buffer switch:Shader");
 PStatCollector GraphicsStateGuardian::_load_vertex_buffer_pcollector("Draw:Transfer data:Vertex buffer");
 PStatCollector GraphicsStateGuardian::_load_vertex_buffer_pcollector("Draw:Transfer data:Vertex buffer");
 PStatCollector GraphicsStateGuardian::_load_index_buffer_pcollector("Draw:Transfer data:Index buffer");
 PStatCollector GraphicsStateGuardian::_load_index_buffer_pcollector("Draw:Transfer data:Index buffer");
+PStatCollector GraphicsStateGuardian::_load_shader_buffer_pcollector("Draw:Transfer data:Shader buffer");
 PStatCollector GraphicsStateGuardian::_create_vertex_buffer_pcollector("Draw:Transfer data:Create Vertex buffer");
 PStatCollector GraphicsStateGuardian::_create_vertex_buffer_pcollector("Draw:Transfer data:Create Vertex buffer");
 PStatCollector GraphicsStateGuardian::_create_index_buffer_pcollector("Draw:Transfer data:Create Index buffer");
 PStatCollector GraphicsStateGuardian::_create_index_buffer_pcollector("Draw:Transfer data:Create Index buffer");
+PStatCollector GraphicsStateGuardian::_create_shader_buffer_pcollector("Draw:Transfer data:Create Shader buffer");
 PStatCollector GraphicsStateGuardian::_load_texture_pcollector("Draw:Transfer data:Texture");
 PStatCollector GraphicsStateGuardian::_load_texture_pcollector("Draw:Transfer data:Texture");
 PStatCollector GraphicsStateGuardian::_data_transferred_pcollector("Data transferred");
 PStatCollector GraphicsStateGuardian::_data_transferred_pcollector("Data transferred");
 PStatCollector GraphicsStateGuardian::_texmgrmem_total_pcollector("Texture manager");
 PStatCollector GraphicsStateGuardian::_texmgrmem_total_pcollector("Texture manager");
@@ -104,6 +107,7 @@ PStatCollector GraphicsStateGuardian::_prepare_geom_pcollector("Draw:Prepare:Geo
 PStatCollector GraphicsStateGuardian::_prepare_shader_pcollector("Draw:Prepare:Shader");
 PStatCollector GraphicsStateGuardian::_prepare_shader_pcollector("Draw:Prepare:Shader");
 PStatCollector GraphicsStateGuardian::_prepare_vertex_buffer_pcollector("Draw:Prepare:Vertex buffer");
 PStatCollector GraphicsStateGuardian::_prepare_vertex_buffer_pcollector("Draw:Prepare:Vertex buffer");
 PStatCollector GraphicsStateGuardian::_prepare_index_buffer_pcollector("Draw:Prepare:Index buffer");
 PStatCollector GraphicsStateGuardian::_prepare_index_buffer_pcollector("Draw:Prepare:Index buffer");
+PStatCollector GraphicsStateGuardian::_prepare_shader_buffer_pcollector("Draw:Prepare:Shader buffer");
 
 
 PStatCollector GraphicsStateGuardian::_draw_set_state_transform_pcollector("Draw:Set State:Transform");
 PStatCollector GraphicsStateGuardian::_draw_set_state_transform_pcollector("Draw:Set State:Transform");
 PStatCollector GraphicsStateGuardian::_draw_set_state_alpha_test_pcollector("Draw:Set State:Alpha test");
 PStatCollector GraphicsStateGuardian::_draw_set_state_alpha_test_pcollector("Draw:Set State:Alpha test");
@@ -657,6 +661,22 @@ void GraphicsStateGuardian::
 release_index_buffer(IndexBufferContext *) {
 release_index_buffer(IndexBufferContext *) {
 }
 }
 
 
+/**
+ * Prepares the indicated buffer for retained-mode rendering.
+ */
+BufferContext *GraphicsStateGuardian::
+prepare_shader_buffer(ShaderBuffer *) {
+  return (BufferContext *)NULL;
+}
+
+/**
+ * Frees the resources previously allocated via a call to prepare_data(),
+ * including deleting the BufferContext itself, if necessary.
+ */
+void GraphicsStateGuardian::
+release_shader_buffer(BufferContext *) {
+}
+
 /**
 /**
  * Begins a new occlusion query.  After this call, you may call
  * Begins a new occlusion query.  After this call, you may call
  * begin_draw_primitives() and draw_triangles()/draw_whatever() repeatedly.
  * begin_draw_primitives() and draw_triangles()/draw_whatever() repeatedly.

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

@@ -88,6 +88,7 @@ PUBLISHED:
   INLINE int release_all_geoms();
   INLINE int release_all_geoms();
   INLINE int release_all_vertex_buffers();
   INLINE int release_all_vertex_buffers();
   INLINE int release_all_index_buffers();
   INLINE int release_all_index_buffers();
+  INLINE int release_all_shader_buffers();
 
 
   INLINE void set_active(bool active);
   INLINE void set_active(bool active);
   INLINE bool is_active() const;
   INLINE bool is_active() const;
@@ -307,6 +308,9 @@ public:
   virtual IndexBufferContext *prepare_index_buffer(GeomPrimitive *data);
   virtual IndexBufferContext *prepare_index_buffer(GeomPrimitive *data);
   virtual void release_index_buffer(IndexBufferContext *ibc);
   virtual void release_index_buffer(IndexBufferContext *ibc);
 
 
+  virtual BufferContext *prepare_shader_buffer(ShaderBuffer *data);
+  virtual void release_shader_buffer(BufferContext *ibc);
+
   virtual void begin_occlusion_query();
   virtual void begin_occlusion_query();
   virtual PT(OcclusionQueryContext) end_occlusion_query();
   virtual PT(OcclusionQueryContext) end_occlusion_query();
 
 
@@ -640,10 +644,13 @@ public:
   // Statistics
   // Statistics
   static PStatCollector _vertex_buffer_switch_pcollector;
   static PStatCollector _vertex_buffer_switch_pcollector;
   static PStatCollector _index_buffer_switch_pcollector;
   static PStatCollector _index_buffer_switch_pcollector;
+  static PStatCollector _shader_buffer_switch_pcollector;
   static PStatCollector _load_vertex_buffer_pcollector;
   static PStatCollector _load_vertex_buffer_pcollector;
   static PStatCollector _load_index_buffer_pcollector;
   static PStatCollector _load_index_buffer_pcollector;
+  static PStatCollector _load_shader_buffer_pcollector;
   static PStatCollector _create_vertex_buffer_pcollector;
   static PStatCollector _create_vertex_buffer_pcollector;
   static PStatCollector _create_index_buffer_pcollector;
   static PStatCollector _create_index_buffer_pcollector;
+  static PStatCollector _create_shader_buffer_pcollector;
   static PStatCollector _load_texture_pcollector;
   static PStatCollector _load_texture_pcollector;
   static PStatCollector _data_transferred_pcollector;
   static PStatCollector _data_transferred_pcollector;
   static PStatCollector _texmgrmem_total_pcollector;
   static PStatCollector _texmgrmem_total_pcollector;
@@ -680,6 +687,7 @@ public:
   static PStatCollector _prepare_shader_pcollector;
   static PStatCollector _prepare_shader_pcollector;
   static PStatCollector _prepare_vertex_buffer_pcollector;
   static PStatCollector _prepare_vertex_buffer_pcollector;
   static PStatCollector _prepare_index_buffer_pcollector;
   static PStatCollector _prepare_index_buffer_pcollector;
+  static PStatCollector _prepare_shader_buffer_pcollector;
 
 
   // A whole slew of collectors to measure the cost of individual state
   // A whole slew of collectors to measure the cost of individual state
   // changes.  These are disabled by default.
   // changes.  These are disabled by default.

+ 25 - 0
panda/src/glstuff/glBufferContext_src.I

@@ -0,0 +1,25 @@
+/**
+ * PANDA 3D SOFTWARE
+ * Copyright (c) Carnegie Mellon University.  All rights reserved.
+ *
+ * All use of this software is subject to the terms of the revised BSD
+ * license.  You should have received a copy of this license along
+ * with this source code in a file named "LICENSE."
+ *
+ * @file glBufferContext_src.I
+ * @author rdb
+ * @date 2016-12-12
+ */
+
+/**
+ *
+ */
+INLINE CLP(BufferContext)::
+CLP(BufferContext)(CLP(GraphicsStateGuardian) *glgsg,
+                   PreparedGraphicsObjects *pgo) :
+  BufferContext(&pgo->_sbuffer_residency),
+  AdaptiveLruPage(0),
+  _glgsg(glgsg)
+{
+  _index = 0;
+}

+ 49 - 0
panda/src/glstuff/glBufferContext_src.cxx

@@ -0,0 +1,49 @@
+/**
+ * PANDA 3D SOFTWARE
+ * Copyright (c) Carnegie Mellon University.  All rights reserved.
+ *
+ * All use of this software is subject to the terms of the revised BSD
+ * license.  You should have received a copy of this license along
+ * with this source code in a file named "LICENSE."
+ *
+ * @file glBufferContext_src.cxx
+ * @author rdb
+ * @date 2016-12-12
+ */
+
+TypeHandle CLP(BufferContext)::_type_handle;
+
+/**
+ * Evicts the page from the LRU.  Called internally when the LRU determines
+ * that it is full.  May also be called externally when necessary to
+ * explicitly evict the page.
+ *
+ * It is legal for this method to either evict the page as requested, do
+ * nothing (in which case the eviction will be requested again at the next
+ * epoch), or requeue itself on the tail of the queue (in which case the
+ * eviction will be requested again much later).
+ */
+void CLP(BufferContext)::
+evict_lru() {
+  dequeue_lru();
+
+  // Make sure the buffer is unbound before we delete it.
+  if (_glgsg->_current_ibuffer_index == _index) {
+    if (GLCAT.is_debug() && gl_debug_buffers) {
+      GLCAT.debug()
+        << "unbinding index buffer\n";
+    }
+    _glgsg->_glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+    _glgsg->_current_ibuffer_index = 0;
+  }
+
+  // Free the buffer.
+  _glgsg->_glDeleteBuffers(1, &_index);
+
+  // We still need a valid index number, though, in case we want to re-load
+  // the buffer later.
+  _glgsg->_glGenBuffers(1, &_index);
+
+  update_data_size_bytes(0);
+  set_resident(false);
+}

+ 52 - 0
panda/src/glstuff/glBufferContext_src.h

@@ -0,0 +1,52 @@
+/**
+ * PANDA 3D SOFTWARE
+ * Copyright (c) Carnegie Mellon University.  All rights reserved.
+ *
+ * All use of this software is subject to the terms of the revised BSD
+ * license.  You should have received a copy of this license along
+ * with this source code in a file named "LICENSE."
+ *
+ * @file glBufferContext_src.h
+ * @author rdb
+ * @date 2016-12-12
+ */
+
+#include "pandabase.h"
+#include "bufferContext.h"
+#include "deletedChain.h"
+
+/**
+ * Caches a GeomPrimitive on the GL as a buffer object.
+ */
+class EXPCL_GL CLP(BufferContext) : public BufferContext, public AdaptiveLruPage {
+public:
+  INLINE CLP(BufferContext)(CLP(GraphicsStateGuardian) *glgsg,
+                            PreparedGraphicsObjects *pgo);
+  ALLOC_DELETED_CHAIN(CLP(BufferContext));
+
+  virtual void evict_lru();
+
+  CLP(GraphicsStateGuardian) *_glgsg;
+
+  // This is the GL "name" of the data object.
+  GLuint _index;
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    BufferContext::init_type();
+    register_type(_type_handle, CLASSPREFIX_QUOTED "BufferContext",
+                  BufferContext::get_class_type());
+  }
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+
+private:
+  static TypeHandle _type_handle;
+};
+
+#include "glBufferContext_src.I"

+ 160 - 9
panda/src/glstuff/glGraphicsStateGuardian_src.cxx

@@ -1796,7 +1796,7 @@ reset() {
   }
   }
 #endif
 #endif
 
 
-#ifndef OPENGLES
+#ifndef OPENGLES_1
   // Check for uniform buffers.
   // Check for uniform buffers.
 #ifdef OPENGLES
 #ifdef OPENGLES
   if (is_at_least_gl_version(3, 1) || has_extension("GL_ARB_uniform_buffer_object")) {
   if (is_at_least_gl_version(3, 1) || has_extension("GL_ARB_uniform_buffer_object")) {
@@ -1810,11 +1810,29 @@ reset() {
        get_extension_func("glGetActiveUniformBlockiv");
        get_extension_func("glGetActiveUniformBlockiv");
     _glGetActiveUniformBlockName = (PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC)
     _glGetActiveUniformBlockName = (PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC)
        get_extension_func("glGetActiveUniformBlockName");
        get_extension_func("glGetActiveUniformBlockName");
+  } else {
+    _supports_uniform_buffers = false;
+  }
+
+#ifndef OPENGLES
+  // Check for SSBOs.
+  if (is_at_least_gl_version(4, 3) || has_extension("ARB_shader_storage_buffer_object")) {
+    _supports_shader_buffers = true;
+    _glGetProgramInterfaceiv = (PFNGLGETPROGRAMINTERFACEIVPROC)
+       get_extension_func("glGetProgramInterfaceiv");
+    _glGetProgramResourceName = (PFNGLGETPROGRAMRESOURCENAMEPROC)
+       get_extension_func("glGetProgramResourceName");
+    _glGetProgramResourceiv = (PFNGLGETPROGRAMRESOURCEIVPROC)
+       get_extension_func("glGetProgramResourceiv");
+  } else
+#endif
+  {
+    _supports_shader_buffers = false;
+  }
 
 
+  if (_supports_uniform_buffers || _supports_shader_buffers) {
     _glBindBufferBase = (PFNGLBINDBUFFERBASEPROC)
     _glBindBufferBase = (PFNGLBINDBUFFERBASEPROC)
       get_extension_func("glBindBufferBase");
       get_extension_func("glBindBufferBase");
-  } else {
-    _supports_uniform_buffers = false;
   }
   }
 #endif
 #endif
 
 
@@ -3031,6 +3049,9 @@ reset() {
   _current_vertex_buffers.clear();
   _current_vertex_buffers.clear();
   _current_vertex_format.clear();
   _current_vertex_format.clear();
   memset(_vertex_attrib_columns, 0, sizeof(const GeomVertexColumn *) * 32);
   memset(_vertex_attrib_columns, 0, sizeof(const GeomVertexColumn *) * 32);
+
+  _current_sbuffer_index = 0;
+  _current_sbuffer_base.clear();
 #endif
 #endif
 
 
   report_my_gl_errors();
   report_my_gl_errors();
@@ -5873,6 +5894,122 @@ setup_primitive(const unsigned char *&client_pointer,
   return true;
   return true;
 }
 }
 
 
+#ifndef OPENGLES
+/**
+ * Creates a new retained-mode representation of the given data, and returns a
+ * newly-allocated BufferContext pointer to reference it.  It is the
+ * responsibility of the calling function to later call release_shader_buffer()
+ * with this same pointer (which will also delete the pointer).
+ *
+ * This function should not be called directly to prepare a buffer.  Instead,
+ * call ShaderBuffer::prepare().
+ */
+BufferContext *CLP(GraphicsStateGuardian)::
+prepare_shader_buffer(ShaderBuffer *data) {
+  if (_supports_shader_buffers) {
+    PStatGPUTimer timer(this, _prepare_shader_buffer_pcollector);
+
+    CLP(BufferContext) *gbc = new CLP(BufferContext)(this, _prepared_objects);
+    _glGenBuffers(1, &gbc->_index);
+
+    if (GLCAT.is_debug() && gl_debug_buffers) {
+      GLCAT.debug()
+        << "creating shader buffer " << (int)gbc->_index << ": "<< *data << "\n";
+    }
+    _glBindBuffer(GL_SHADER_STORAGE_BUFFER, gbc->_index);
+    _current_sbuffer_index = gbc->_index;
+
+    if (_use_object_labels) {
+      string name = data->get_name();
+      _glObjectLabel(GL_SHADER_STORAGE_BUFFER, gbc->_index, name.size(), name.data());
+    }
+
+    uint64_t num_bytes = data->get_data_size_bytes();
+    if (_supports_buffer_storage) {
+      _glBufferStorage(GL_SHADER_STORAGE_BUFFER, num_bytes, data->get_initial_data(), 0);
+    } else {
+      _glBufferData(GL_SHADER_STORAGE_BUFFER, num_bytes, data->get_initial_data(), get_usage(data->get_usage_hint()));
+    }
+
+    gbc->enqueue_lru(&_prepared_objects->_graphics_memory_lru);
+
+    report_my_gl_errors();
+    return gbc;
+  }
+
+  return NULL;
+}
+
+/**
+ * Binds the given shader buffer to the given binding slot.
+ */
+void CLP(GraphicsStateGuardian)::
+apply_shader_buffer(GLuint base, ShaderBuffer *buffer) {
+  GLuint index = 0;
+  if (buffer != NULL) {
+    BufferContext *bc = buffer->prepare_now(get_prepared_objects(), this);
+    if (bc != NULL) {
+      CLP(BufferContext) *gbc = DCAST(CLP(BufferContext), bc);
+      index = gbc->_index;
+      gbc->set_active(true);
+    }
+  }
+
+  if (base >= _current_sbuffer_base.size()) {
+    _current_sbuffer_base.resize(base + 1, 0);
+  }
+
+  if (_current_sbuffer_base[base] != index) {
+    if (GLCAT.is_spam() && gl_debug_buffers) {
+      GLCAT.spam()
+        << "binding shader buffer " << (int)index
+        << " to index " << base << "\n";
+    }
+    _glBindBufferBase(GL_SHADER_STORAGE_BUFFER, base, index);
+    _current_sbuffer_base[base] = index;
+    _current_sbuffer_index = index;
+
+    report_my_gl_errors();
+  }
+}
+
+/**
+ * Frees the GL resources previously allocated for the data.  This function
+ * should never be called directly; instead, call Data::release() (or simply
+ * let the Data destruct).
+ */
+void CLP(GraphicsStateGuardian)::
+release_shader_buffer(BufferContext *bc) {
+  nassertv(_supports_buffers);
+
+  CLP(BufferContext) *gbc = DCAST(CLP(BufferContext), bc);
+
+  if (GLCAT.is_debug() && gl_debug_buffers) {
+    GLCAT.debug()
+      << "deleting shader buffer " << (int)gbc->_index << "\n";
+  }
+
+  // Make sure the buffer is unbound before we delete it.  Not strictly
+  // necessary according to the OpenGL spec, but it might help out a flaky
+  // driver, and we need to keep our internal state consistent anyway.
+  if (_current_sbuffer_index == gbc->_index) {
+    if (GLCAT.is_spam() && gl_debug_buffers) {
+      GLCAT.spam()
+        << "unbinding shader buffer\n";
+    }
+    _glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
+    _current_sbuffer_index = 0;
+  }
+
+  _glDeleteBuffers(1, &gbc->_index);
+  report_my_gl_errors();
+
+  gbc->_index = 0;
+
+  delete gbc;
+}
+#endif
+
 #ifndef OPENGLES
 #ifndef OPENGLES
 /**
 /**
  * Begins a new occlusion query.  After this call, you may call
  * Begins a new occlusion query.  After this call, you may call
@@ -6499,7 +6636,7 @@ do_issue_shade_model() {
 
 
 #ifndef OPENGLES_1
 #ifndef OPENGLES_1
 /**
 /**
- *
+ * Called when the current ShaderAttrib state has changed.
  */
  */
 void CLP(GraphicsStateGuardian)::
 void CLP(GraphicsStateGuardian)::
 do_issue_shader() {
 do_issue_shader() {
@@ -6512,21 +6649,30 @@ do_issue_shader() {
     shader = _default_shader;
     shader = _default_shader;
     nassertv(shader != NULL);
     nassertv(shader != NULL);
   }
   }
-
 #endif
 #endif
+
   if (shader) {
   if (shader) {
-    context = shader->prepare_now(get_prepared_objects(), this);
+    if (_current_shader != shader) {
+      context = shader->prepare_now(get_prepared_objects(), this);
+    } else {
+      context = _current_shader_context;
+    }
   }
   }
+
 #ifndef SUPPORT_FIXED_FUNCTION
 #ifndef SUPPORT_FIXED_FUNCTION
   // If it failed, try applying the default shader.
   // If it failed, try applying the default shader.
   if (shader != _default_shader && (context == 0 || !context->valid())) {
   if (shader != _default_shader && (context == 0 || !context->valid())) {
     shader = _default_shader;
     shader = _default_shader;
     nassertv(shader != NULL);
     nassertv(shader != NULL);
-    context = shader->prepare_now(get_prepared_objects(), this);
+    if (_current_shader != shader) {
+      context = shader->prepare_now(get_prepared_objects(), this);
+    } else {
+      context = _current_shader_context;
+    }
   }
   }
 #endif
 #endif
 
 
-  if (context == 0 || (context->valid() == false)) {
+  if (context == 0 || !context->valid()) {
     if (_current_shader_context != 0) {
     if (_current_shader_context != 0) {
       _current_shader_context->unbind();
       _current_shader_context->unbind();
       _current_shader = 0;
       _current_shader = 0;
@@ -6538,12 +6684,16 @@ do_issue_shader() {
       // bind the new one.
       // bind the new one.
       if (_current_shader_context != NULL &&
       if (_current_shader_context != NULL &&
           _current_shader->get_language() != shader->get_language()) {
           _current_shader->get_language() != shader->get_language()) {
+        // If it's a different type of shader, make sure to unbind the old.
         _current_shader_context->unbind();
         _current_shader_context->unbind();
       }
       }
       context->bind();
       context->bind();
       _current_shader = shader;
       _current_shader = shader;
-      _current_shader_context = context;
     }
     }
+
+    // Bind the shader storage buffers.
+    context->update_shader_buffer_bindings(_current_shader_context);
+    _current_shader_context = context;
   }
   }
 
 
 #ifndef OPENGLES
 #ifndef OPENGLES
@@ -10116,6 +10266,7 @@ set_state_and_transform(const RenderState *target,
   }
   }
 #endif
 #endif
 
 
+  // Update all of the state that is bound to the shader program.
   if (_current_shader_context != NULL) {
   if (_current_shader_context != NULL) {
     _current_shader_context->set_state_and_transform(target, transform, _projection_mat);
     _current_shader_context->set_state_and_transform(target, transform, _projection_mat);
   }
   }

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

@@ -344,6 +344,12 @@ public:
                        const GeomPrimitivePipelineReader *reader,
                        const GeomPrimitivePipelineReader *reader,
                        bool force);
                        bool force);
 
 
+#ifndef OPENGLES
+  virtual BufferContext *prepare_shader_buffer(ShaderBuffer *data);
+  void apply_shader_buffer(GLuint base, ShaderBuffer *buffer);
+  virtual void release_shader_buffer(BufferContext *bc);
+#endif
+
 #ifndef OPENGLES
 #ifndef OPENGLES
   virtual void begin_occlusion_query();
   virtual void begin_occlusion_query();
   virtual PT(OcclusionQueryContext) end_occlusion_query();
   virtual PT(OcclusionQueryContext) end_occlusion_query();
@@ -685,6 +691,9 @@ protected:
   bool _use_vertex_attrib_binding;
   bool _use_vertex_attrib_binding;
   CPT(GeomVertexFormat) _current_vertex_format;
   CPT(GeomVertexFormat) _current_vertex_format;
   const GeomVertexColumn *_vertex_attrib_columns[32];
   const GeomVertexColumn *_vertex_attrib_columns[32];
+
+  GLuint _current_sbuffer_index;
+  pvector<GLuint> _current_sbuffer_base;
 #endif
 #endif
 
 
   int _active_texture_stage;
   int _active_texture_stage;
@@ -811,6 +820,7 @@ public:
 
 
 #ifndef OPENGLES_1
 #ifndef OPENGLES_1
   bool _supports_uniform_buffers;
   bool _supports_uniform_buffers;
+  bool _supports_shader_buffers;
   PFNGLBINDBUFFERBASEPROC _glBindBufferBase;
   PFNGLBINDBUFFERBASEPROC _glBindBufferBase;
 
 
   bool _supports_buffer_storage;
   bool _supports_buffer_storage;
@@ -995,6 +1005,9 @@ public:
   PFNGLMAKETEXTUREHANDLENONRESIDENTPROC _glMakeTextureHandleNonResident;
   PFNGLMAKETEXTUREHANDLENONRESIDENTPROC _glMakeTextureHandleNonResident;
   PFNGLUNIFORMHANDLEUI64PROC _glUniformHandleui64;
   PFNGLUNIFORMHANDLEUI64PROC _glUniformHandleui64;
   PFNGLUNIFORMHANDLEUI64VPROC _glUniformHandleui64v;
   PFNGLUNIFORMHANDLEUI64VPROC _glUniformHandleui64v;
+  PFNGLGETPROGRAMINTERFACEIVPROC _glGetProgramInterfaceiv;
+  PFNGLGETPROGRAMRESOURCENAMEPROC _glGetProgramResourceName;
+  PFNGLGETPROGRAMRESOURCEIVPROC _glGetProgramResourceiv;
 #endif  // !OPENGLES
 #endif  // !OPENGLES
 
 
   GLenum _edge_clamp;
   GLenum _edge_clamp;
@@ -1090,6 +1103,7 @@ private:
 
 
   friend class CLP(VertexBufferContext);
   friend class CLP(VertexBufferContext);
   friend class CLP(IndexBufferContext);
   friend class CLP(IndexBufferContext);
+  friend class CLP(BufferContext);
   friend class CLP(ShaderContext);
   friend class CLP(ShaderContext);
   friend class CLP(CgShaderContext);
   friend class CLP(CgShaderContext);
   friend class CLP(GraphicsBuffer);
   friend class CLP(GraphicsBuffer);

+ 53 - 0
panda/src/glstuff/glShaderContext_src.cxx

@@ -324,6 +324,34 @@ CLP(ShaderContext)(CLP(GraphicsStateGuardian) *glgsg, Shader *s) : ShaderContext
     }
     }
   }
   }
 
 
+#ifndef OPENGLES
+  // Get the used shader storage blocks.
+  if (_glgsg->_supports_shader_buffers) {
+    GLint block_count = 0, block_maxlength = 0;
+
+    _glgsg->_glGetProgramInterfaceiv(_glsl_program, GL_SHADER_STORAGE_BLOCK, GL_ACTIVE_RESOURCES, &block_count);
+    _glgsg->_glGetProgramInterfaceiv(_glsl_program, GL_SHADER_STORAGE_BLOCK, GL_MAX_NAME_LENGTH, &block_maxlength);
+
+    block_maxlength = max(64, block_maxlength);
+    char *block_name_cstr = (char *)alloca(block_maxlength);
+
+    for (int i = 0; i < block_count; ++i) {
+      block_name_cstr[0] = 0;
+      _glgsg->_glGetProgramResourceName(_glsl_program, GL_SHADER_STORAGE_BLOCK, i, block_maxlength, NULL, block_name_cstr);
+
+      const GLenum props[] = {GL_BUFFER_BINDING, GL_BUFFER_DATA_SIZE};
+      GLint values[2];
+      _glgsg->_glGetProgramResourceiv(_glsl_program, GL_SHADER_STORAGE_BLOCK, i, 2, props, 2, NULL, values);
+
+      StorageBlock block;
+      block._name = InternalName::make(block_name_cstr);
+      block._binding_index = values[0];
+      block._min_size = values[1];
+      _storage_blocks.push_back(block);
+    }
+  }
+#endif
+
   // Bind the program, so that we can call glUniform1i for the textures.
   // Bind the program, so that we can call glUniform1i for the textures.
   _glgsg->_glUseProgram(_glsl_program);
   _glgsg->_glUseProgram(_glsl_program);
 
 
@@ -2659,6 +2687,31 @@ update_shader_texture_bindings(ShaderContext *prev) {
   _glgsg->report_my_gl_errors();
   _glgsg->report_my_gl_errors();
 }
 }
 
 
+/**
+ * Updates the shader buffer bindings for this shader.
+ */
+void CLP(ShaderContext)::
+update_shader_buffer_bindings(ShaderContext *prev) {
+#ifndef OPENGLES
+  // Update the shader storage buffer bindings.
+  const ShaderAttrib *attrib = _glgsg->_target_shader;
+
+  for (size_t i = 0; i < _storage_blocks.size(); ++i) {
+    StorageBlock &block = _storage_blocks[i];
+
+    ShaderBuffer *buffer = attrib->get_shader_input_buffer(block._name);
+#ifndef NDEBUG
+    if (buffer->get_data_size_bytes() < block._min_size) {
+      GLCAT.error()
+        << "cannot bind " << *buffer << " to shader because it is too small"
+           " (expected at least " << block._min_size << " bytes)\n";
+    }
+#endif
+    _glgsg->apply_shader_buffer(block._binding_index, buffer);
+  }
+#endif
+}
+
 /**
 /**
  * This subroutine prints the infolog for a shader.
  * This subroutine prints the infolog for a shader.
  */
  */

+ 12 - 0
panda/src/glstuff/glShaderContext_src.h

@@ -55,6 +55,7 @@ public:
   bool update_shader_vertex_arrays(ShaderContext *prev, bool force);
   bool update_shader_vertex_arrays(ShaderContext *prev, bool force);
   void disable_shader_texture_bindings() OVERRIDE;
   void disable_shader_texture_bindings() OVERRIDE;
   void update_shader_texture_bindings(ShaderContext *prev) OVERRIDE;
   void update_shader_texture_bindings(ShaderContext *prev) OVERRIDE;
+  void update_shader_buffer_bindings(ShaderContext *prev) OVERRIDE;
 
 
   INLINE bool uses_standard_vertex_arrays(void);
   INLINE bool uses_standard_vertex_arrays(void);
   INLINE bool uses_custom_vertex_arrays(void);
   INLINE bool uses_custom_vertex_arrays(void);
@@ -87,6 +88,17 @@ private:
   pmap<GLint, GLuint64> _glsl_uniform_handles;
   pmap<GLint, GLuint64> _glsl_uniform_handles;
 #endif
 #endif
 
 
+#ifndef OPENGLES
+  struct StorageBlock {
+    CPT(InternalName) _name;
+    GLuint _binding_index;
+    GLint _min_size;
+  };
+  typedef pvector<StorageBlock> StorageBlocks;
+  StorageBlocks _storage_blocks;
+  BitArray _used_storage_bindings;
+#endif
+
   struct ImageInput {
   struct ImageInput {
     CPT(InternalName) _name;
     CPT(InternalName) _name;
     CLP(TextureContext) *_gtc;
     CLP(TextureContext) *_gtc;

+ 1 - 0
panda/src/glstuff/glmisc_src.cxx

@@ -331,6 +331,7 @@ void CLP(init_classes)() {
   CLP(SamplerContext)::init_type();
   CLP(SamplerContext)::init_type();
 #endif
 #endif
   CLP(VertexBufferContext)::init_type();
   CLP(VertexBufferContext)::init_type();
+  CLP(BufferContext)::init_type();
   CLP(GraphicsBuffer)::init_type();
   CLP(GraphicsBuffer)::init_type();
 
 
 #ifndef OPENGLES
 #ifndef OPENGLES

+ 1 - 0
panda/src/glstuff/glstuff_src.cxx

@@ -21,6 +21,7 @@
 #include "glSamplerContext_src.cxx"
 #include "glSamplerContext_src.cxx"
 #include "glVertexBufferContext_src.cxx"
 #include "glVertexBufferContext_src.cxx"
 #include "glIndexBufferContext_src.cxx"
 #include "glIndexBufferContext_src.cxx"
+#include "glBufferContext_src.cxx"
 #include "glOcclusionQueryContext_src.cxx"
 #include "glOcclusionQueryContext_src.cxx"
 #include "glTimerQueryContext_src.cxx"
 #include "glTimerQueryContext_src.cxx"
 #include "glLatencyQueryContext_src.cxx"
 #include "glLatencyQueryContext_src.cxx"

+ 1 - 0
panda/src/glstuff/glstuff_src.h

@@ -33,6 +33,7 @@
 #include "glSamplerContext_src.h"
 #include "glSamplerContext_src.h"
 #include "glVertexBufferContext_src.h"
 #include "glVertexBufferContext_src.h"
 #include "glIndexBufferContext_src.h"
 #include "glIndexBufferContext_src.h"
+#include "glBufferContext_src.h"
 #include "glOcclusionQueryContext_src.h"
 #include "glOcclusionQueryContext_src.h"
 #include "glTimerQueryContext_src.h"
 #include "glTimerQueryContext_src.h"
 #include "glLatencyQueryContext_src.h"
 #include "glLatencyQueryContext_src.h"

+ 1 - 0
panda/src/gobj/p3gobj_composite2.cxx

@@ -5,6 +5,7 @@
 #include "samplerContext.cxx"
 #include "samplerContext.cxx"
 #include "samplerState.cxx"
 #include "samplerState.cxx"
 #include "savedContext.cxx"
 #include "savedContext.cxx"
+#include "shaderBuffer.cxx"
 #include "shaderContext.cxx"
 #include "shaderContext.cxx"
 #include "shader.cxx"
 #include "shader.cxx"
 #include "simpleAllocator.cxx"
 #include "simpleAllocator.cxx"

+ 5 - 2
panda/src/gobj/preparedGraphicsObjects.I

@@ -45,6 +45,7 @@ release_all() {
   _texture_residency.set_levels();
   _texture_residency.set_levels();
   _vbuffer_residency.set_levels();
   _vbuffer_residency.set_levels();
   _ibuffer_residency.set_levels();
   _ibuffer_residency.set_levels();
+  _sbuffer_residency.set_levels();
 }
 }
 
 
 /**
 /**
@@ -58,7 +59,8 @@ get_num_queued() const {
           get_num_queued_geoms() +
           get_num_queued_geoms() +
           get_num_queued_shaders() +
           get_num_queued_shaders() +
           get_num_queued_vertex_buffers() +
           get_num_queued_vertex_buffers() +
-          get_num_queued_index_buffers());
+          get_num_queued_index_buffers() +
+          get_num_queued_shader_buffers());
 }
 }
 
 
 /**
 /**
@@ -72,7 +74,8 @@ get_num_prepared() const {
           get_num_prepared_geoms() +
           get_num_prepared_geoms() +
           get_num_prepared_shaders() +
           get_num_prepared_shaders() +
           get_num_prepared_vertex_buffers() +
           get_num_prepared_vertex_buffers() +
-          get_num_prepared_index_buffers());
+          get_num_prepared_index_buffers() +
+          get_num_prepared_shader_buffers());
 }
 }
 
 
 /**
 /**

+ 165 - 0
panda/src/gobj/preparedGraphicsObjects.cxx

@@ -41,6 +41,7 @@ PreparedGraphicsObjects() :
   _texture_residency(_name, "texture"),
   _texture_residency(_name, "texture"),
   _vbuffer_residency(_name, "vbuffer"),
   _vbuffer_residency(_name, "vbuffer"),
   _ibuffer_residency(_name, "ibuffer"),
   _ibuffer_residency(_name, "ibuffer"),
+  _sbuffer_residency(_name, "sbuffer"),
   _graphics_memory_lru("graphics_memory_lru", graphics_memory_limit),
   _graphics_memory_lru("graphics_memory_lru", graphics_memory_limit),
   _sampler_object_lru("sampler_object_lru", sampler_object_limit)
   _sampler_object_lru("sampler_object_lru", sampler_object_limit)
 {
 {
@@ -121,6 +122,16 @@ PreparedGraphicsObjects::
     delete ibc;
     delete ibc;
   }
   }
   _released_index_buffers.clear();
   _released_index_buffers.clear();
+
+  release_all_shader_buffers();
+  Buffers::iterator bci;
+  for (bci = _released_shader_buffers.begin();
+       bci != _released_shader_buffers.end();
+       ++bci) {
+    BufferContext *bc = (BufferContext *)(*bci);
+    delete bc;
+  }
+  _released_shader_buffers.clear();
 }
 }
 
 
 /**
 /**
@@ -167,6 +178,9 @@ show_residency_trackers(ostream &out) const {
 
 
   out << "\nIndex buffers:\n";
   out << "\nIndex buffers:\n";
   _ibuffer_residency.write(out, 2);
   _ibuffer_residency.write(out, 2);
+
+  out << "\nShader buffers:\n";
+  _sbuffer_residency.write(out, 2);
 }
 }
 
 
 /**
 /**
@@ -1195,6 +1209,155 @@ prepare_index_buffer_now(GeomPrimitive *data, GraphicsStateGuardianBase *gsg) {
   return ibc;
   return ibc;
 }
 }
 
 
+/**
+ * Indicates that a buffer would like to be put on the list to be prepared
+ * when the GSG is next ready to do this (presumably at the next frame).
+ */
+void PreparedGraphicsObjects::
+enqueue_shader_buffer(ShaderBuffer *data) {
+  ReMutexHolder holder(_lock);
+
+  _enqueued_shader_buffers.insert(data);
+}
+
+/**
+ * Returns true if the index buffer has been queued on this GSG, false
+ * otherwise.
+ */
+bool PreparedGraphicsObjects::
+is_shader_buffer_queued(const ShaderBuffer *data) const {
+  ReMutexHolder holder(_lock);
+
+  EnqueuedShaderBuffers::const_iterator qi = _enqueued_shader_buffers.find((ShaderBuffer *)data);
+  return (qi != _enqueued_shader_buffers.end());
+}
+
+/**
+ * Removes a buffer from the queued list of data arrays to be prepared.
+ * Normally it is not necessary to call this, unless you change your mind
+ * about preparing it at the last minute, since the data will automatically be
+ * dequeued and prepared at the next frame.
+ *
+ * The return value is true if the buffer is successfully dequeued, false if
+ * it had not been queued.
+ */
+bool PreparedGraphicsObjects::
+dequeue_shader_buffer(ShaderBuffer *data) {
+  ReMutexHolder holder(_lock);
+
+  EnqueuedShaderBuffers::iterator qi = _enqueued_shader_buffers.find(data);
+  if (qi != _enqueued_shader_buffers.end()) {
+    _enqueued_shader_buffers.erase(qi);
+    return true;
+  }
+  return false;
+}
+
+/**
+ * Returns true if the index buffer has been prepared on this GSG, false
+ * otherwise.
+ */
+bool PreparedGraphicsObjects::
+is_shader_buffer_prepared(const ShaderBuffer *data) const {
+  return data->is_prepared((PreparedGraphicsObjects *)this);
+}
+
+/**
+ * Indicates that a data context, created by a previous call to
+ * prepare_shader_buffer(), is no longer needed.  The driver resources will not
+ * be freed until some GSG calls update(), indicating it is at a stage where
+ * it is ready to release datas--this prevents conflicts from threading or
+ * multiple GSG's sharing datas (we have no way of knowing which graphics
+ * context is currently active, or what state it's in, at the time
+ * release_shader_buffer is called).
+ */
+void PreparedGraphicsObjects::
+release_shader_buffer(BufferContext *bc) {
+  ReMutexHolder holder(_lock);
+
+  bool removed = (_prepared_shader_buffers.erase(bc) != 0);
+  nassertv(removed);
+
+  _released_shader_buffers.insert(bc);
+}
+
+/**
+ * Releases all datas at once.  This will force them to be reloaded into data
+ * memory for all GSG's that share this object.  Returns the number of datas
+ * released.
+ */
+int PreparedGraphicsObjects::
+release_all_shader_buffers() {
+  ReMutexHolder holder(_lock);
+
+  int num_shader_buffers = (int)_prepared_shader_buffers.size() + (int)_enqueued_shader_buffers.size();
+
+  Buffers::iterator bci;
+  for (bci = _prepared_shader_buffers.begin();
+       bci != _prepared_shader_buffers.end();
+       ++bci) {
+
+    BufferContext *bc = (BufferContext *)(*bci);
+    _released_shader_buffers.insert(bc);
+  }
+
+  _prepared_shader_buffers.clear();
+  _enqueued_shader_buffers.clear();
+
+  return num_shader_buffers;
+}
+
+/**
+ * Returns the number of index buffers that have been enqueued to be prepared
+ * on this GSG.
+ */
+int PreparedGraphicsObjects::
+get_num_queued_shader_buffers() const {
+  return _enqueued_shader_buffers.size();
+}
+
+/**
+ * Returns the number of index buffers that have already been prepared on this
+ * GSG.
+ */
+int PreparedGraphicsObjects::
+get_num_prepared_shader_buffers() const {
+  return _prepared_shader_buffers.size();
+}
+
+/**
+ * Immediately creates a new BufferContext for the indicated data and
+ * returns it.  This assumes that the GraphicsStateGuardian is the currently
+ * active rendering context and that it is ready to accept new datas.  If this
+ * is not necessarily the case, you should use enqueue_shader_buffer() instead.
+ *
+ * Normally, this function is not called directly.  Call Data::prepare_now()
+ * instead.
+ *
+ * The BufferContext contains all of the pertinent information needed by
+ * the GSG to keep track of this one particular data, and will exist as long
+ * as the data is ready to be rendered.
+ *
+ * When either the Data or the PreparedGraphicsObjects object destructs, the
+ * BufferContext will be deleted.
+ */
+BufferContext *PreparedGraphicsObjects::
+prepare_shader_buffer_now(ShaderBuffer *data, GraphicsStateGuardianBase *gsg) {
+  ReMutexHolder holder(_lock);
+
+  // Ask the GSG to create a brand new BufferContext.  There might be
+  // several GSG's sharing the same set of datas; if so, it doesn't matter
+  // which of them creates the context (since they're all shared anyway).
+  BufferContext *bc = gsg->prepare_shader_buffer(data);
+
+  if (bc != (BufferContext *)NULL) {
+    bool prepared = _prepared_shader_buffers.insert(bc).second;
+    nassertr(prepared, bc);
+  }
+
+  return bc;
+}
+
 /**
 /**
  * This is called by the GraphicsStateGuardian to indicate that it is about to
  * This is called by the GraphicsStateGuardian to indicate that it is about to
  * begin processing of the frame.
  * begin processing of the frame.
@@ -1276,6 +1439,7 @@ begin_frame(GraphicsStateGuardianBase *gsg, Thread *current_thread) {
   _texture_residency.begin_frame(current_thread);
   _texture_residency.begin_frame(current_thread);
   _vbuffer_residency.begin_frame(current_thread);
   _vbuffer_residency.begin_frame(current_thread);
   _ibuffer_residency.begin_frame(current_thread);
   _ibuffer_residency.begin_frame(current_thread);
+  _sbuffer_residency.begin_frame(current_thread);
 
 
   // Now prepare all the textures, geoms, and buffers awaiting preparation.
   // Now prepare all the textures, geoms, and buffers awaiting preparation.
   EnqueuedTextures::iterator qti;
   EnqueuedTextures::iterator qti;
@@ -1359,6 +1523,7 @@ end_frame(Thread *current_thread) {
   _texture_residency.end_frame(current_thread);
   _texture_residency.end_frame(current_thread);
   _vbuffer_residency.end_frame(current_thread);
   _vbuffer_residency.end_frame(current_thread);
   _ibuffer_residency.end_frame(current_thread);
   _ibuffer_residency.end_frame(current_thread);
+  _sbuffer_residency.end_frame(current_thread);
 }
 }
 
 
 /**
 /**

+ 19 - 0
panda/src/gobj/preparedGraphicsObjects.h

@@ -22,6 +22,7 @@
 #include "geomVertexArrayData.h"
 #include "geomVertexArrayData.h"
 #include "geomPrimitive.h"
 #include "geomPrimitive.h"
 #include "shader.h"
 #include "shader.h"
+#include "shaderBuffer.h"
 #include "pointerTo.h"
 #include "pointerTo.h"
 #include "pStatCollector.h"
 #include "pStatCollector.h"
 #include "pset.h"
 #include "pset.h"
@@ -35,6 +36,7 @@ class GeomContext;
 class ShaderContext;
 class ShaderContext;
 class VertexBufferContext;
 class VertexBufferContext;
 class IndexBufferContext;
 class IndexBufferContext;
+class BufferContext;
 class GraphicsStateGuardianBase;
 class GraphicsStateGuardianBase;
 
 
 /**
 /**
@@ -142,6 +144,19 @@ PUBLISHED:
   prepare_index_buffer_now(GeomPrimitive *data,
   prepare_index_buffer_now(GeomPrimitive *data,
                            GraphicsStateGuardianBase *gsg);
                            GraphicsStateGuardianBase *gsg);
 
 
+  void enqueue_shader_buffer(ShaderBuffer *data);
+  bool is_shader_buffer_queued(const ShaderBuffer *data) const;
+  bool dequeue_shader_buffer(ShaderBuffer *data);
+  bool is_shader_buffer_prepared(const ShaderBuffer *data) const;
+  void release_shader_buffer(BufferContext *bc);
+  int release_all_shader_buffers();
+  int get_num_queued_shader_buffers() const;
+  int get_num_prepared_shader_buffers() const;
+
+  BufferContext *
+  prepare_shader_buffer_now(ShaderBuffer *data,
+                            GraphicsStateGuardianBase *gsg);
+
 public:
 public:
   void begin_frame(GraphicsStateGuardianBase *gsg,
   void begin_frame(GraphicsStateGuardianBase *gsg,
                    Thread *current_thread);
                    Thread *current_thread);
@@ -160,6 +175,7 @@ private:
   typedef phash_set<BufferContext *, pointer_hash> Buffers;
   typedef phash_set<BufferContext *, pointer_hash> Buffers;
   typedef phash_set< PT(GeomVertexArrayData) > EnqueuedVertexBuffers;
   typedef phash_set< PT(GeomVertexArrayData) > EnqueuedVertexBuffers;
   typedef phash_set< PT(GeomPrimitive) > EnqueuedIndexBuffers;
   typedef phash_set< PT(GeomPrimitive) > EnqueuedIndexBuffers;
+  typedef phash_set< PT(ShaderBuffer) > EnqueuedShaderBuffers;
 
 
   // Sampler states are stored a little bit differently, as they are mapped by
   // Sampler states are stored a little bit differently, as they are mapped by
   // value and can't store the list of prepared samplers.
   // value and can't store the list of prepared samplers.
@@ -207,6 +223,8 @@ private:
   EnqueuedVertexBuffers _enqueued_vertex_buffers;
   EnqueuedVertexBuffers _enqueued_vertex_buffers;
   Buffers _prepared_index_buffers, _released_index_buffers;
   Buffers _prepared_index_buffers, _released_index_buffers;
   EnqueuedIndexBuffers _enqueued_index_buffers;
   EnqueuedIndexBuffers _enqueued_index_buffers;
+  Buffers _prepared_shader_buffers, _released_shader_buffers;
+  EnqueuedShaderBuffers _enqueued_shader_buffers;
 
 
   BufferCache _vertex_buffer_cache;
   BufferCache _vertex_buffer_cache;
   BufferCacheLRU _vertex_buffer_cache_lru;
   BufferCacheLRU _vertex_buffer_cache_lru;
@@ -220,6 +238,7 @@ public:
   BufferResidencyTracker _texture_residency;
   BufferResidencyTracker _texture_residency;
   BufferResidencyTracker _vbuffer_residency;
   BufferResidencyTracker _vbuffer_residency;
   BufferResidencyTracker _ibuffer_residency;
   BufferResidencyTracker _ibuffer_residency;
+  BufferResidencyTracker _sbuffer_residency;
 
 
   AdaptiveLru _graphics_memory_lru;
   AdaptiveLru _graphics_memory_lru;
   SimpleLru _sampler_object_lru;
   SimpleLru _sampler_object_lru;

+ 63 - 0
panda/src/gobj/shaderBuffer.I

@@ -0,0 +1,63 @@
+/**
+ * PANDA 3D SOFTWARE
+ * Copyright (c) Carnegie Mellon University.  All rights reserved.
+ *
+ * All use of this software is subject to the terms of the revised BSD
+ * license.  You should have received a copy of this license along
+ * with this source code in a file named "LICENSE."
+ *
+ * @file shaderBuffer.I
+ * @author rdb
+ * @date 2016-12-12
+ */
+
+/**
+ * Creates an uninitialized buffer object with the given size.  For now, these
+ * parameters cannot be modified, but this may change in the future.
+ */
+INLINE ShaderBuffer::
+ShaderBuffer(const string &name, uint64_t size, UsageHint usage_hint) :
+  Namable(name),
+  _data_size_bytes(size),
+  _usage_hint(usage_hint) {
+}
+
+/**
+ * Creates a buffer object initialized with the given data.  For now, these
+ * parameters cannot be modified, but this may change in the future.
+ */
+INLINE ShaderBuffer::
+ShaderBuffer(const string &name, pvector<unsigned char> initial_data, UsageHint usage_hint) :
+  Namable(name),
+  _data_size_bytes(initial_data.size()),
+  _usage_hint(usage_hint),
+  _initial_data(initial_data) {
+}
+
+/**
+ * Returns the buffer size in bytes.
+ */
+INLINE uint64_t ShaderBuffer::
+get_data_size_bytes() const {
+  return _data_size_bytes;
+}
+
+/**
+ * Returns the buffer usage hint.
+ */
+INLINE GeomEnums::UsageHint ShaderBuffer::
+get_usage_hint() const {
+  return _usage_hint;
+}
+
+/**
+ * Returns a pointer to the initial buffer data, or NULL if not specified.
+ */
+INLINE const unsigned char *ShaderBuffer::
+get_initial_data() const {
+  if (_initial_data.empty()) {
+    return NULL;
+  } else {
+    return &_initial_data[0];
+  }
+}

+ 193 - 0
panda/src/gobj/shaderBuffer.cxx

@@ -0,0 +1,193 @@
+/**
+ * PANDA 3D SOFTWARE
+ * Copyright (c) Carnegie Mellon University.  All rights reserved.
+ *
+ * All use of this software is subject to the terms of the revised BSD
+ * license.  You should have received a copy of this license along
+ * with this source code in a file named "LICENSE."
+ *
+ * @file shaderBuffer.cxx
+ * @author rdb
+ * @date 2016-12-12
+ */
+
+#include "shaderBuffer.h"
+#include "preparedGraphicsObjects.h"
+
+TypeHandle ShaderBuffer::_type_handle;
+
+/**
+ *
+ */
+void ShaderBuffer::
+output(ostream &out) const {
+  out << "buffer " << get_name() << ", " << _data_size_bytes << "B, " << _usage_hint;
+}
+
+/**
+ * Indicates that the data should be enqueued to be prepared in the indicated
+ * prepared_objects at the beginning of the next frame.  This will ensure the
+ * data is already loaded into the GSG if it is expected to be rendered soon.
+ *
+ * Use this function instead of prepare_now() to preload datas from a user
+ * interface standpoint.
+ */
+void ShaderBuffer::
+prepare(PreparedGraphicsObjects *prepared_objects) {
+  prepared_objects->enqueue_shader_buffer(this);
+}
+
+/**
+ * Returns true if the data has already been prepared or enqueued for
+ * preparation on the indicated GSG, false otherwise.
+ */
+bool ShaderBuffer::
+is_prepared(PreparedGraphicsObjects *prepared_objects) const {
+  if (_contexts == (Contexts *)NULL) {
+    return false;
+  }
+  Contexts::const_iterator ci;
+  ci = _contexts->find(prepared_objects);
+  if (ci != _contexts->end()) {
+    return true;
+  }
+  return prepared_objects->is_shader_buffer_queued(this);
+}
+
+/**
+ * Creates a context for the data on the particular GSG, if it does not
+ * already exist.  Returns the new (or old) BufferContext.  This assumes
+ * that the GraphicsStateGuardian is the currently active rendering context
+ * and that it is ready to accept new datas.  If this is not necessarily the
+ * case, you should use prepare() instead.
+ *
+ * Normally, this is not called directly except by the GraphicsStateGuardian;
+ * a data does not need to be explicitly prepared by the user before it may be
+ * rendered.
+ */
+BufferContext *ShaderBuffer::
+prepare_now(PreparedGraphicsObjects *prepared_objects,
+            GraphicsStateGuardianBase *gsg) {
+  if (_contexts == (Contexts *)NULL) {
+    _contexts = new Contexts;
+  }
+  Contexts::const_iterator ci;
+  ci = _contexts->find(prepared_objects);
+  if (ci != _contexts->end()) {
+    return (*ci).second;
+  }
+
+  BufferContext *vbc = prepared_objects->prepare_shader_buffer_now(this, gsg);
+  if (vbc != (BufferContext *)NULL) {
+    (*_contexts)[prepared_objects] = vbc;
+  }
+  return vbc;
+}
+
+/**
+ * Frees the data context only on the indicated object, if it exists there.
+ * Returns true if it was released, false if it had not been prepared.
+ */
+bool ShaderBuffer::
+release(PreparedGraphicsObjects *prepared_objects) {
+  if (_contexts != (Contexts *)NULL) {
+    Contexts::iterator ci;
+    ci = _contexts->find(prepared_objects);
+    if (ci != _contexts->end()) {
+      BufferContext *vbc = (*ci).second;
+      prepared_objects->release_shader_buffer(vbc);
+      return true;
+    }
+  }
+
+  // Maybe it wasn't prepared yet, but it's about to be.
+  return prepared_objects->dequeue_shader_buffer(this);
+}
+
+/**
+ * Frees the context allocated on all objects for which the data has been
+ * declared.  Returns the number of contexts which have been freed.
+ */
+int ShaderBuffer::
+release_all() {
+  int num_freed = 0;
+
+  if (_contexts != (Contexts *)NULL) {
+    // We have to traverse a copy of the _contexts list, because the
+    // PreparedGraphicsObjects object will call clear_prepared() in response
+    // to each release_shader_buffer(), and we don't want to be modifying the
+    // _contexts list while we're traversing it.
+    Contexts temp = *_contexts;
+    num_freed = (int)_contexts->size();
+
+    Contexts::const_iterator ci;
+    for (ci = temp.begin(); ci != temp.end(); ++ci) {
+      PreparedGraphicsObjects *prepared_objects = (*ci).first;
+      BufferContext *vbc = (*ci).second;
+      prepared_objects->release_shader_buffer(vbc);
+    }
+
+    // Now that we've called release_shader_buffer() on every known context,
+    // the _contexts list should have completely emptied itself.
+    nassertr(_contexts == NULL, num_freed);
+  }
+
+  return num_freed;
+}
+
+/**
+ * Tells the BamReader how to create objects of type ParamValue.
+ */
+void ShaderBuffer::
+register_with_read_factory() {
+  BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
+}
+
+/**
+ * Writes the contents of this object to the datagram for shipping out to a
+ * Bam file.
+ */
+void ShaderBuffer::
+write_datagram(BamWriter *manager, Datagram &dg) {
+  dg.add_string(get_name());
+  dg.add_uint64(_data_size_bytes);
+  dg.add_uint8(_usage_hint);
+  dg.add_bool(!_initial_data.empty());
+  dg.append_data(_initial_data.data(), _initial_data.size());
+}
+
+/**
+ * This function is called by the BamReader's factory when a new object of
+ * type ParamValue is encountered in the Bam file.  It should create the
+ * ParamValue and extract its information from the file.
+ */
+TypedWritable *ShaderBuffer::
+make_from_bam(const FactoryParams &params) {
+  ShaderBuffer *param = new ShaderBuffer;
+  DatagramIterator scan;
+  BamReader *manager;
+
+  parse_params(params, scan, manager);
+  param->fillin(scan, manager);
+
+  return param;
+}
+
+/**
+ * This internal function is called by make_from_bam to read in all of the
+ * relevant data from the BamFile for the new ParamValue.
+ */
+void ShaderBuffer::
+fillin(DatagramIterator &scan, BamReader *manager) {
+  set_name(scan.get_string());
+  _data_size_bytes = scan.get_uint64();
+  _usage_hint = (UsageHint)scan.get_uint8();
+
+  if (scan.get_bool() && _data_size_bytes > 0) {
+    nassertv_always(_data_size_bytes <= scan.get_remaining_size());
+    _initial_data.resize(_data_size_bytes);
+    scan.extract_bytes(&_initial_data[0], _data_size_bytes);
+  } else {
+    _initial_data.clear();
+  }
+}

+ 97 - 0
panda/src/gobj/shaderBuffer.h

@@ -0,0 +1,97 @@
+/**
+ * PANDA 3D SOFTWARE
+ * Copyright (c) Carnegie Mellon University.  All rights reserved.
+ *
+ * All use of this software is subject to the terms of the revised BSD
+ * license.  You should have received a copy of this license along
+ * with this source code in a file named "LICENSE."
+ *
+ * @file shaderBuffer.h
+ * @author rdb
+ * @date 2016-12-12
+ */
+
+#ifndef SHADERBUFFER_H
+#define SHADERBUFFER_H
+
+#include "pandabase.h"
+#include "namable.h"
+#include "geomEnums.h"
+
+class BufferContext;
+class PreparedGraphicsObjects;
+
+/**
+ * This is a generic buffer object that lives in graphics memory.
+ */
+class EXPCL_PANDA_GOBJ ShaderBuffer : public TypedWritableReferenceCount, public Namable, public GeomEnums {
+private:
+  INLINE ShaderBuffer() DEFAULT_CTOR;
+
+PUBLISHED:
+  INLINE ShaderBuffer(const string &name, uint64_t size, UsageHint usage_hint);
+  INLINE ShaderBuffer(const string &name, pvector<unsigned char> initial_data, UsageHint usage_hint);
+
+public:
+  INLINE uint64_t get_data_size_bytes() const;
+  INLINE UsageHint get_usage_hint() const;
+  INLINE const unsigned char *get_initial_data() const;
+
+  virtual void output(ostream &out) const;
+
+PUBLISHED:
+  MAKE_PROPERTY(data_size_bytes, get_data_size_bytes);
+  MAKE_PROPERTY(usage_hint, get_usage_hint);
+
+  void prepare(PreparedGraphicsObjects *prepared_objects);
+  bool is_prepared(PreparedGraphicsObjects *prepared_objects) const;
+
+  BufferContext *prepare_now(PreparedGraphicsObjects *prepared_objects,
+                             GraphicsStateGuardianBase *gsg);
+  bool release(PreparedGraphicsObjects *prepared_objects);
+  int release_all();
+
+private:
+  uint64_t _data_size_bytes;
+  UsageHint _usage_hint;
+  pvector<unsigned char> _initial_data;
+
+  typedef pmap<PreparedGraphicsObjects *, BufferContext *> Contexts;
+  Contexts *_contexts;
+
+public:
+  static void register_with_read_factory();
+  virtual void write_datagram(BamWriter *manager, Datagram &dg);
+
+protected:
+  static TypedWritable *make_from_bam(const FactoryParams &params);
+  void fillin(DatagramIterator &scan, BamReader *manager);
+
+public:
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    TypedWritableReferenceCount::init_type();
+    Namable::init_type();
+    register_type(_type_handle, "ShaderBuffer",
+                  TypedWritableReferenceCount::get_class_type(),
+                  Namable::get_class_type());
+  }
+
+private:
+  static TypeHandle _type_handle;
+};
+
+INLINE ostream &operator << (ostream &out, const ShaderBuffer &m) {
+  m.output(out);
+  return out;
+}
+
+#include "shaderBuffer.I"
+
+#endif

+ 1 - 0
panda/src/gobj/shaderContext.h

@@ -42,6 +42,7 @@ public:
   INLINE virtual bool update_shader_vertex_arrays(ShaderContext *prev, bool force) { return false; };
   INLINE virtual bool update_shader_vertex_arrays(ShaderContext *prev, bool force) { return false; };
   INLINE virtual void disable_shader_texture_bindings() {};
   INLINE virtual void disable_shader_texture_bindings() {};
   INLINE virtual void update_shader_texture_bindings(ShaderContext *prev) {};
   INLINE virtual void update_shader_texture_bindings(ShaderContext *prev) {};
+  INLINE virtual void update_shader_buffer_bindings(ShaderContext *prev) {};
 
 
   INLINE virtual bool uses_standard_vertex_arrays(void) { return true; };
   INLINE virtual bool uses_standard_vertex_arrays(void) { return true; };
   INLINE virtual bool uses_custom_vertex_arrays(void) { return false; };
   INLINE virtual bool uses_custom_vertex_arrays(void) { return false; };

+ 5 - 0
panda/src/gsgbase/graphicsStateGuardianBase.h

@@ -31,6 +31,7 @@ class GraphicsOutputBase;
 
 
 class VertexBufferContext;
 class VertexBufferContext;
 class IndexBufferContext;
 class IndexBufferContext;
+class BufferContext;
 class GeomContext;
 class GeomContext;
 class GeomNode;
 class GeomNode;
 class Geom;
 class Geom;
@@ -57,6 +58,7 @@ class SamplerContext;
 class SamplerState;
 class SamplerState;
 class Shader;
 class Shader;
 class ShaderContext;
 class ShaderContext;
+class ShaderBuffer;
 class RenderState;
 class RenderState;
 class TransformState;
 class TransformState;
 class Material;
 class Material;
@@ -162,6 +164,9 @@ public:
   virtual IndexBufferContext *prepare_index_buffer(GeomPrimitive *data)=0;
   virtual IndexBufferContext *prepare_index_buffer(GeomPrimitive *data)=0;
   virtual void release_index_buffer(IndexBufferContext *ibc)=0;
   virtual void release_index_buffer(IndexBufferContext *ibc)=0;
 
 
+  virtual BufferContext *prepare_shader_buffer(ShaderBuffer *data)=0;
+  virtual void release_shader_buffer(BufferContext *ibc)=0;
+
   virtual void dispatch_compute(int size_x, int size_y, int size_z)=0;
   virtual void dispatch_compute(int size_x, int size_y, int size_z)=0;
 
 
   virtual PT(GeomMunger) get_geom_munger(const RenderState *state,
   virtual PT(GeomMunger) get_geom_munger(const RenderState *state,

+ 8 - 0
panda/src/pgraph/nodePath.I

@@ -1253,6 +1253,14 @@ set_shader_input(CPT_InternalName id, Texture *tex, bool read, bool write, int z
   set_shader_input(new ShaderInput(id, tex, read, write, z, n, priority));
   set_shader_input(new ShaderInput(id, tex, read, write, z, n, priority));
 }
 }
 
 
+/**
+ *
+ */
+INLINE void NodePath::
+set_shader_input(CPT_InternalName id, ShaderBuffer *buf, int priority) {
+  set_shader_input(new ShaderInput(id, buf, priority));
+}
+
 /**
 /**
  *
  *
  */
  */

+ 2 - 0
panda/src/pgraph/nodePath.h

@@ -61,6 +61,7 @@ class GlobPattern;
 class PreparedGraphicsObjects;
 class PreparedGraphicsObjects;
 class SamplerState;
 class SamplerState;
 class Shader;
 class Shader;
+class ShaderBuffer;
 class ShaderInput;
 class ShaderInput;
 
 
 //
 //
@@ -632,6 +633,7 @@ PUBLISHED:
   INLINE void set_shader_input(CPT_InternalName id, Texture *tex, int priority=0);
   INLINE void set_shader_input(CPT_InternalName id, Texture *tex, int priority=0);
   INLINE void set_shader_input(CPT_InternalName id, Texture *tex, const SamplerState &sampler, int priority=0);
   INLINE void set_shader_input(CPT_InternalName id, Texture *tex, const SamplerState &sampler, int priority=0);
   INLINE void set_shader_input(CPT_InternalName id, Texture *tex, bool read, bool write, int z=-1, int n=0, int priority=0);
   INLINE void set_shader_input(CPT_InternalName id, Texture *tex, bool read, bool write, int z=-1, int n=0, int priority=0);
+  INLINE void set_shader_input(CPT_InternalName id, ShaderBuffer *buf, int priority=0);
   INLINE void set_shader_input(CPT_InternalName id, const NodePath &np, int priority=0);
   INLINE void set_shader_input(CPT_InternalName id, const NodePath &np, int priority=0);
   INLINE void set_shader_input(CPT_InternalName id, const PTA_float &v, int priority=0);
   INLINE void set_shader_input(CPT_InternalName id, const PTA_float &v, int priority=0);
   INLINE void set_shader_input(CPT_InternalName id, const PTA_double &v, int priority=0);
   INLINE void set_shader_input(CPT_InternalName id, const PTA_double &v, int priority=0);

+ 29 - 0
panda/src/pgraph/shaderAttrib.cxx

@@ -25,6 +25,7 @@
 #include "datagram.h"
 #include "datagram.h"
 #include "datagramIterator.h"
 #include "datagramIterator.h"
 #include "nodePath.h"
 #include "nodePath.h"
+#include "shaderBuffer.h"
 
 
 TypeHandle ShaderAttrib::_type_handle;
 TypeHandle ShaderAttrib::_type_handle;
 int ShaderAttrib::_attrib_slot;
 int ShaderAttrib::_attrib_slot;
@@ -461,6 +462,34 @@ get_shader_input_matrix(const InternalName *id, LMatrix4 &matrix) const {
   }
   }
 }
 }
 
 
+/**
+ * Returns the ShaderInput as a ShaderBuffer.  Assertion fails if there is
+ * none, or if it is not a ShaderBuffer.
+ */
+ShaderBuffer *ShaderAttrib::
+get_shader_input_buffer(const InternalName *id) const {
+  Inputs::const_iterator i = _inputs.find(id);
+  if (i == _inputs.end()) {
+    ostringstream strm;
+    strm << "Shader input " << id->get_name() << " is not present.\n";
+    nassert_raise(strm.str());
+    return NULL;
+  } else {
+    const ShaderInput *p = (*i).second;
+
+    if (p->get_value_type() == ShaderInput::M_buffer) {
+      ShaderBuffer *value;
+      DCAST_INTO_R(value, p->_value, NULL);
+      return value;
+    }
+
+    ostringstream strm;
+    strm << "Shader input " << id->get_name() << " is not a ShaderBuffer.\n";
+    nassert_raise(strm.str());
+    return NULL;
+  }
+}
+
 /**
 /**
  * Returns the shader object associated with the node.  If get_override
  * Returns the shader object associated with the node.  If get_override
  * returns true, but get_shader returns NULL, that means that this attribute
  * returns true, but get_shader returns NULL, that means that this attribute

+ 1 - 0
panda/src/pgraph/shaderAttrib.h

@@ -111,6 +111,7 @@ PUBLISHED:
   Texture *get_shader_input_texture(const InternalName *id, SamplerState *sampler=NULL) const;
   Texture *get_shader_input_texture(const InternalName *id, SamplerState *sampler=NULL) const;
   const Shader::ShaderPtrData *get_shader_input_ptr(const InternalName *id) const;
   const Shader::ShaderPtrData *get_shader_input_ptr(const InternalName *id) const;
   const LMatrix4 &get_shader_input_matrix(const InternalName *id, LMatrix4 &matrix) const;
   const LMatrix4 &get_shader_input_matrix(const InternalName *id, LMatrix4 &matrix) const;
+  ShaderBuffer *get_shader_input_buffer(const InternalName *id) const;
 
 
   static void register_with_read_factory();
   static void register_with_read_factory();
 
 

+ 12 - 0
panda/src/pgraph/shaderInput.I

@@ -55,6 +55,18 @@ ShaderInput(CPT_InternalName name, ParamValueBase *param, int priority) :
 {
 {
 }
 }
 
 
+/**
+ *
+ */
+INLINE ShaderInput::
+ShaderInput(CPT_InternalName name, ShaderBuffer *buf, int priority) :
+  _name(MOVE(name)),
+  _type(M_buffer),
+  _priority(priority),
+  _value(buf)
+{
+}
+
 /**
 /**
  *
  *
  */
  */

+ 6 - 1
panda/src/pgraph/shaderInput.h

@@ -31,6 +31,7 @@
 #include "samplerState.h"
 #include "samplerState.h"
 #include "shader.h"
 #include "shader.h"
 #include "texture.h"
 #include "texture.h"
+#include "shaderBuffer.h"
 
 
 /**
 /**
  * This is a small container class that can hold any one of the value types
  * This is a small container class that can hold any one of the value types
@@ -52,6 +53,7 @@ PUBLISHED:
   INLINE ShaderInput(CPT_InternalName name, int priority=0);
   INLINE ShaderInput(CPT_InternalName name, int priority=0);
   INLINE ShaderInput(CPT_InternalName name, Texture *tex, int priority=0);
   INLINE ShaderInput(CPT_InternalName name, Texture *tex, int priority=0);
   INLINE ShaderInput(CPT_InternalName name, ParamValueBase *param, int priority=0);
   INLINE ShaderInput(CPT_InternalName name, ParamValueBase *param, int priority=0);
+  INLINE ShaderInput(CPT_InternalName name, ShaderBuffer *buf, int priority=0);
   INLINE ShaderInput(CPT_InternalName name, const PTA_float &ptr, int priority=0);
   INLINE ShaderInput(CPT_InternalName name, const PTA_float &ptr, int priority=0);
   INLINE ShaderInput(CPT_InternalName name, const PTA_LVecBase4f &ptr, int priority=0);
   INLINE ShaderInput(CPT_InternalName name, const PTA_LVecBase4f &ptr, int priority=0);
   INLINE ShaderInput(CPT_InternalName name, const PTA_LVecBase3f &ptr, int priority=0);
   INLINE ShaderInput(CPT_InternalName name, const PTA_LVecBase3f &ptr, int priority=0);
@@ -96,7 +98,8 @@ PUBLISHED:
     M_numeric,
     M_numeric,
     M_texture_sampler,
     M_texture_sampler,
     M_param,
     M_param,
-    M_texture_image
+    M_texture_image,
+    M_buffer,
   };
   };
 
 
   INLINE const InternalName *get_name() const;
   INLINE const InternalName *get_name() const;
@@ -123,6 +126,8 @@ private:
   int _priority;
   int _priority;
   int _type;
   int _type;
 
 
+  friend class ShaderAttrib;
+
 public:
 public:
   static TypeHandle get_class_type() {
   static TypeHandle get_class_type() {
     return _type_handle;
     return _type_handle;