Răsfoiți Sursa

Add hardware instancing support

rdb 16 ani în urmă
părinte
comite
c136904fa9

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

@@ -651,6 +651,19 @@ get_supports_two_sided_stencil() const {
   return _supports_two_sided_stencil;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsStateGuardian::get_supports_geometry_instancing
+//       Access: Published
+//  Description: Returns true if this particular GSG supports
+//               hardware geometry instancing: the ability to render
+//               multiple copies of a model. In OpenGL, this is
+//               done using the EXT_draw_instanced extension.
+////////////////////////////////////////////////////////////////////
+INLINE bool GraphicsStateGuardian::
+get_supports_geometry_instancing() const {
+  return _supports_geometry_instancing;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: GraphicsStateGuardian::get_maximum_simultaneous_render_targets
 //       Access: Published

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

@@ -210,6 +210,7 @@ GraphicsStateGuardian(CoordinateSystem internal_coordinate_system,
   _supports_stencil = false;
   _supports_stencil_wrap = false;
   _supports_two_sided_stencil = false;
+  _supports_geometry_instancing = false;
 
   _maximum_simultaneous_render_targets = 1;
 

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

@@ -143,6 +143,7 @@ PUBLISHED:
   INLINE bool get_supports_glsl() const;
   INLINE bool get_supports_stencil() const;
   INLINE bool get_supports_two_sided_stencil() const;
+  INLINE bool get_supports_geometry_instancing() const;
 
   INLINE int get_maximum_simultaneous_render_targets() const;
 
@@ -467,6 +468,7 @@ protected:
   bool _supports_stencil;
   bool _supports_stencil_wrap;
   bool _supports_two_sided_stencil;
+  bool _supports_geometry_instancing;
 
   int _maximum_simultaneous_render_targets;
 

+ 180 - 49
panda/src/glstuff/glGraphicsStateGuardian_src.cxx

@@ -1454,6 +1454,26 @@ reset() {
     _glActiveStencilFaceEXT = 0;
   }
 
+#ifndef OPENGLES
+  // Some drivers expose one, some expose the other. ARB seems to be the newer one.
+  if (has_extension("GL_ARB_draw_instanced")) {
+    _glDrawArraysInstanced = (PFNGLDRAWARRAYSINSTANCEDPROC)
+      get_extension_func(GLPREFIX_QUOTED, "DrawArraysInstancedARB");
+    _glDrawElementsInstanced = (PFNGLDRAWELEMENTSINSTANCEDPROC)
+      get_extension_func(GLPREFIX_QUOTED, "DrawElementsInstancedARB");
+    _supports_geometry_instancing = true;
+  } else if (has_extension("GL_EXT_draw_instanced")) {
+    _glDrawArraysInstanced = (PFNGLDRAWARRAYSINSTANCEDPROC)
+      get_extension_func(GLPREFIX_QUOTED, "DrawArraysInstancedEXT");
+    _glDrawElementsInstanced = (PFNGLDRAWELEMENTSINSTANCEDPROC)
+      get_extension_func(GLPREFIX_QUOTED, "DrawElementsInstancedEXT");
+    _supports_geometry_instancing = true;
+  } else {
+    _glDrawElementsInstanced = 0;
+    _glDrawArraysInstanced = 0;
+  }
+#endif
+
   _auto_rescale_normal = false;
 
   // Ensure the initial state is what we say it should be (in some
@@ -2728,16 +2748,34 @@ draw_triangles(const GeomPrimitivePipelineReader *reader, bool force) {
         return false;
       }
 
-      _glDrawRangeElements(GL_TRIANGLES,
-                           reader->get_min_vertex(),
-                           reader->get_max_vertex(),
-                           num_vertices,
-                           get_numeric_type(reader->get_index_type()),
-                           client_pointer);
+#ifndef OPENGLES
+      if (_supports_geometry_instancing && _instance_count > 0) {
+        _glDrawElementsInstanced(GL_TRIANGLES, num_vertices,
+                                 get_numeric_type(reader->get_index_type()),
+                                 client_pointer, _instance_count);
+      } else
+#endif
+      {
+        _glDrawRangeElements(GL_TRIANGLES,
+                             reader->get_min_vertex(),
+                             reader->get_max_vertex(),
+                             num_vertices,
+                             get_numeric_type(reader->get_index_type()),
+                             client_pointer);
+      }
     } else {
-      GLP(DrawArrays)(GL_TRIANGLES,
-                      reader->get_first_vertex(),
-                      num_vertices);
+#ifndef OPENGLES
+      if (_supports_geometry_instancing && _instance_count > 0) {
+        _glDrawArraysInstanced(GL_TRIANGLES,
+                               reader->get_first_vertex(),
+                               num_vertices, _instance_count);
+      } else
+#endif
+      {
+        GLP(DrawArrays)(GL_TRIANGLES,
+                        reader->get_first_vertex(),
+                        num_vertices);
+      }
     }
   }
 
@@ -2780,16 +2818,34 @@ draw_tristrips(const GeomPrimitivePipelineReader *reader, bool force) {
         if (!setup_primitive(client_pointer, reader, force)) {
           return false;
         }
-        _glDrawRangeElements(GL_TRIANGLE_STRIP,
-                             reader->get_min_vertex(),
-                             reader->get_max_vertex(),
-                             num_vertices,
-                             get_numeric_type(reader->get_index_type()),
-                             client_pointer);
+#ifndef OPENGLES
+        if (_supports_geometry_instancing && _instance_count > 0) {
+          _glDrawElementsInstanced(GL_TRIANGLE_STRIP, num_vertices,
+                                   get_numeric_type(reader->get_index_type()),
+                                   client_pointer, _instance_count);
+        } else
+#endif
+        {
+          _glDrawRangeElements(GL_TRIANGLE_STRIP,
+                               reader->get_min_vertex(),
+                               reader->get_max_vertex(),
+                               num_vertices,
+                               get_numeric_type(reader->get_index_type()),
+                               client_pointer);
+        }
       } else {
-        GLP(DrawArrays)(GL_TRIANGLE_STRIP,
-                        reader->get_first_vertex(),
-                        num_vertices);
+#ifndef OPENGLES
+        if (_supports_geometry_instancing && _instance_count > 0) {
+          _glDrawArraysInstanced(GL_TRIANGLE_STRIP,
+                                 reader->get_first_vertex(),
+                                 num_vertices, _instance_count);
+        } else
+#endif
+        {
+          GLP(DrawArrays)(GL_TRIANGLE_STRIP,
+                          reader->get_first_vertex(),
+                          num_vertices);
+        }
       }
 
     } else {
@@ -2812,11 +2868,21 @@ draw_tristrips(const GeomPrimitivePipelineReader *reader, bool force) {
         unsigned int start = 0;
         for (size_t i = 0; i < ends.size(); i++) {
           _vertices_tristrip_pcollector.add_level(ends[i] - start);
-          _glDrawRangeElements(GL_TRIANGLE_STRIP,
-                               mins.get_data1i(), maxs.get_data1i(),
-                               ends[i] - start,
-                               get_numeric_type(reader->get_index_type()),
-                               client_pointer + start * index_stride);
+#ifndef OPENGLES
+          if (_supports_geometry_instancing && _instance_count > 0) {
+            _glDrawElementsInstanced(GL_TRIANGLE_STRIP, ends[i] - start,
+                                     get_numeric_type(reader->get_index_type()),
+                                     client_pointer + start * index_stride,
+                                     _instance_count);
+          } else
+#endif
+          {
+            _glDrawRangeElements(GL_TRIANGLE_STRIP,
+                                 mins.get_data1i(), maxs.get_data1i(),
+                                 ends[i] - start,
+                                 get_numeric_type(reader->get_index_type()),
+                                 client_pointer + start * index_stride);
+          }
           start = ends[i] + 2;
         }
       } else {
@@ -2824,8 +2890,16 @@ draw_tristrips(const GeomPrimitivePipelineReader *reader, bool force) {
         int first_vertex = reader->get_first_vertex();
         for (size_t i = 0; i < ends.size(); i++) {
           _vertices_tristrip_pcollector.add_level(ends[i] - start);
-          GLP(DrawArrays)(GL_TRIANGLE_STRIP, first_vertex + start,
-                          ends[i] - start);
+#ifndef OPENGLES
+          if (_supports_geometry_instancing && _instance_count > 0) {
+            _glDrawArraysInstanced(GL_TRIANGLE_STRIP, first_vertex + start,
+                                   ends[i] - start, _instance_count);
+          } else
+#endif
+          {
+            GLP(DrawArrays)(GL_TRIANGLE_STRIP, first_vertex + start,
+                            ends[i] - start);
+          }
           start = ends[i] + 2;
         }
       }
@@ -2875,10 +2949,20 @@ draw_trifans(const GeomPrimitivePipelineReader *reader, bool force) {
       unsigned int start = 0;
       for (size_t i = 0; i < ends.size(); i++) {
         _vertices_trifan_pcollector.add_level(ends[i] - start);
-        _glDrawRangeElements(GL_TRIANGLE_FAN,
-                             mins.get_data1i(), maxs.get_data1i(), ends[i] - start,
-                             get_numeric_type(reader->get_index_type()),
-                             client_pointer + start * index_stride);
+#ifndef OPENGLES
+        if (_supports_geometry_instancing && _instance_count > 0) {
+          _glDrawElementsInstanced(GL_TRIANGLE_FAN, ends[i] - start,
+                                   get_numeric_type(reader->get_index_type()),
+                                   client_pointer + start * index_stride,
+                                   _instance_count);
+        } else
+#endif
+        {
+          _glDrawRangeElements(GL_TRIANGLE_FAN,
+                               mins.get_data1i(), maxs.get_data1i(), ends[i] - start,
+                               get_numeric_type(reader->get_index_type()),
+                               client_pointer + start * index_stride);
+        }
         start = ends[i];
       }
     } else {
@@ -2886,8 +2970,16 @@ draw_trifans(const GeomPrimitivePipelineReader *reader, bool force) {
       int first_vertex = reader->get_first_vertex();
       for (size_t i = 0; i < ends.size(); i++) {
         _vertices_trifan_pcollector.add_level(ends[i] - start);
-        GLP(DrawArrays)(GL_TRIANGLE_FAN, first_vertex + start,
-                        ends[i] - start);
+#ifndef OPENGLES
+        if (_supports_geometry_instancing && _instance_count > 0) {
+          _glDrawArraysInstanced(GL_TRIANGLE_FAN, first_vertex + start,
+                                 ends[i] - start, _instance_count);
+        } else
+#endif
+        {
+          GLP(DrawArrays)(GL_TRIANGLE_FAN, first_vertex + start,
+                          ends[i] - start);
+        }
         start = ends[i];
       }
     }
@@ -2926,16 +3018,34 @@ draw_lines(const GeomPrimitivePipelineReader *reader, bool force) {
       if (!setup_primitive(client_pointer, reader, force)) {
         return false;
       }
-      _glDrawRangeElements(GL_LINES,
-                           reader->get_min_vertex(),
-                           reader->get_max_vertex(),
-                           num_vertices,
-                           get_numeric_type(reader->get_index_type()),
-                           client_pointer);
+#ifndef OPENGLES
+      if (_supports_geometry_instancing && _instance_count > 0) {
+        _glDrawElementsInstanced(GL_LINES, num_vertices,
+                                 get_numeric_type(reader->get_index_type()),
+                                 client_pointer, _instance_count);
+      } else
+#endif
+      {
+        _glDrawRangeElements(GL_LINES,
+                             reader->get_min_vertex(),
+                             reader->get_max_vertex(),
+                             num_vertices,
+                             get_numeric_type(reader->get_index_type()),
+                             client_pointer);
+      }
     } else {
-      GLP(DrawArrays)(GL_LINES,
-                      reader->get_first_vertex(),
-                      num_vertices);
+#ifndef OPENGLES
+      if (_supports_geometry_instancing && _instance_count > 0) {
+        _glDrawArraysInstanced(GL_LINES,
+                               reader->get_first_vertex(),
+                               num_vertices, _instance_count);
+      } else
+#endif
+      {
+        GLP(DrawArrays)(GL_LINES,
+                        reader->get_first_vertex(),
+                        num_vertices);
+      }
     }
   }
 
@@ -2982,16 +3092,34 @@ draw_points(const GeomPrimitivePipelineReader *reader, bool force) {
       if (!setup_primitive(client_pointer, reader, force)) {
         return false;
       }
-      _glDrawRangeElements(GL_POINTS,
-                           reader->get_min_vertex(),
-                           reader->get_max_vertex(),
-                           num_vertices,
-                           get_numeric_type(reader->get_index_type()),
-                           client_pointer);
+#ifndef OPENGLES
+      if (_supports_geometry_instancing && _instance_count > 0) {
+        _glDrawElementsInstanced(GL_POINTS, num_vertices,
+                                 get_numeric_type(reader->get_index_type()),
+                                 client_pointer, _instance_count);
+      } else
+#endif
+      {
+        _glDrawRangeElements(GL_POINTS,
+                             reader->get_min_vertex(),
+                             reader->get_max_vertex(),
+                             num_vertices,
+                             get_numeric_type(reader->get_index_type()),
+                             client_pointer);
+      }
     } else {
-      GLP(DrawArrays)(GL_POINTS,
-                      reader->get_first_vertex(),
-                      num_vertices);
+#ifndef OPENGLES
+      if (_supports_geometry_instancing && _instance_count > 0) {
+        _glDrawArraysInstanced(GL_POINTS,
+                               reader->get_first_vertex(),
+                               num_vertices, _instance_count);
+      } else
+#endif
+      {
+        GLP(DrawArrays)(GL_POINTS,
+                        reader->get_first_vertex(),
+                        num_vertices);
+      }
     }
   }
 
@@ -6795,6 +6923,9 @@ set_state_and_transform(const RenderState *target,
   _target_rs = target;
 
   _target_shader = DCAST(ShaderAttrib, _target_rs->get_attrib_def(ShaderAttrib::get_class_slot()));
+#ifndef OPENGLES
+  _instance_count = _target_shader->get_instance_count();
+#endif
 #ifndef OPENGLES_1
   if (_target_shader->auto_shader()) {
     // If we don't have a generated shader, make sure we have a ShaderGenerator, then generate the shader.

+ 7 - 1
panda/src/glstuff/glGraphicsStateGuardian_src.h

@@ -147,7 +147,8 @@ typedef void (APIENTRYP PFNGLVALIDATEPROGRAMPROC) (GLuint program);
 typedef void (APIENTRYP PFNGLVERTEXATTRIBPOINTERPROC) (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid *pointer);
 #endif  // OPENGLES_1
 #ifndef OPENGLES
-typedef void (APIENTRYP PFNGLPROGRAMPARAMETERIEXTPROC) (GLuint program, GLenum pname, GLint value);
+typedef void (APIENTRYP PFNGLDRAWARRAYSINSTANCEDPROC) (GLenum mode, GLint first, GLsizei count, GLsizei primcount);
+typedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDPROC) (GLenum mode, GLsizei count, GLenum type, const GLvoid *indices, GLsizei primcount);
 #endif  // OPENGLES
 #endif  // __EDG__
 
@@ -625,6 +626,8 @@ public:
 #endif  // OPENGLES_1
 #ifndef OPENGLES
   PFNGLPROGRAMPARAMETERIEXTPROC _glProgramParameteri;
+  PFNGLDRAWARRAYSINSTANCEDPROC _glDrawArraysInstanced;
+  PFNGLDRAWELEMENTSINSTANCEDPROC _glDrawElementsInstanced;
 #endif  // OPENGLES
 
   GLenum _edge_clamp;
@@ -633,6 +636,9 @@ public:
   GLenum _mirror_clamp;
   GLenum _mirror_edge_clamp;
   GLenum _mirror_border_clamp;
+#ifndef OPENGLES
+  GLsizei _instance_count;
+#endif
 
   LightMutex _lock;
   typedef pvector<GLuint> DeletedDisplayLists;

+ 42 - 0
panda/src/pgraph/nodePath.cxx

@@ -3709,6 +3709,24 @@ get_shader_input(InternalName *id) const {
   return NULL;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: NodePath::get_instance_count
+//       Access: Published
+//  Description: Returns the geometry instance count, or 0 if
+//               disabled. See set_instance_count.
+////////////////////////////////////////////////////////////////////
+const int NodePath::
+get_instance_count() const {
+  nassertr_always(!is_empty(), NULL);
+  const RenderAttrib *attrib =
+    node()->get_attrib(ShaderAttrib::get_class_slot());
+  if (attrib != (const RenderAttrib *)NULL) {
+    const ShaderAttrib *sa = DCAST(ShaderAttrib, attrib);
+    return sa->get_instance_count();
+  }
+  return 0;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: NodePath::clear_shader_input
 //       Access: Published
@@ -3826,6 +3844,30 @@ clear_shader_input(const string &id) {
   clear_shader_input(InternalName::make(id));
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: NodePath::set_instance_count
+//       Access: Published
+//  Description: Sets the geometry instance count, or 0 if
+//               geometry instancing should be disabled. Do not
+//               confuse with instanceTo which only applies to
+//               animation instancing.
+////////////////////////////////////////////////////////////////////
+void NodePath::
+set_instance_count(int instance_count) {
+  nassertv_always(!is_empty());
+
+  const RenderAttrib *attrib =
+    node()->get_attrib(ShaderAttrib::get_class_slot());
+  if (attrib != (const RenderAttrib *)NULL) {
+    const ShaderAttrib *sa = DCAST(ShaderAttrib, attrib);
+    node()->set_attrib(sa->set_instance_count(instance_count));
+  } else {
+    // Create a new ShaderAttrib for this node.
+    CPT(ShaderAttrib) sa = DCAST(ShaderAttrib, ShaderAttrib::make());
+    node()->set_attrib(sa->set_instance_count(instance_count));
+  }
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: NodePath::set_tex_transform
 //       Access: Published

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

@@ -606,10 +606,12 @@ PUBLISHED:
   void set_shader_input(const string &id, double n1=0, double n2=0, double n3=0, double n4=1, int priority=0);
   void clear_shader_input(InternalName *id);
   void clear_shader_input(const string &id);
+  void set_instance_count(int instance_count);
 
   const Shader *get_shader() const;
   const ShaderInput *get_shader_input(InternalName *id) const;
   const ShaderInput *get_shader_input(const string &id) const;
+  const int get_instance_count() const;
   
   void set_tex_transform(TextureStage *stage, const TransformState *transform);
   void clear_tex_transform();

+ 14 - 1
panda/src/pgraph/shaderAttrib.I

@@ -25,7 +25,8 @@ ShaderAttrib() :
   _auto_shader(false),
   _has_shader(false),
   _flags(0),
-  _has_flags(0)
+  _has_flags(0),
+  _instance_count(0)
 {
 }
 
@@ -42,6 +43,7 @@ ShaderAttrib(const ShaderAttrib &copy) :
   _has_shader(copy._has_shader),
   _flags(copy._flags),
   _has_flags(copy._has_flags),
+  _instance_count(copy._instance_count),
   _inputs(copy._inputs)
 {
 }
@@ -79,6 +81,17 @@ get_shader_priority() const {
   return _shader_priority;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: ShaderAttrib::get_instance_count
+//       Access: Published
+//  Description: Returns the number of geometry instances. A value
+//               of 0 means not to use instancing at all.
+////////////////////////////////////////////////////////////////////
+INLINE int ShaderAttrib::
+get_instance_count() const {
+  return _instance_count;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: ShaderAttrib::get_flag
 //       Access: Published

+ 20 - 1
panda/src/pgraph/shaderAttrib.cxx

@@ -30,7 +30,6 @@ int ShaderAttrib::_attrib_slot;
 //               the use of shaders (it does not clear out all shader
 //               data, however.)
 ////////////////////////////////////////////////////////////////////
-
 CPT(RenderAttrib) ShaderAttrib::
 make_off() {
   static CPT(RenderAttrib) _off_attrib;
@@ -259,6 +258,21 @@ set_shader_input(const string &id, double n1, double n2, double n3, double n4, i
   return set_shader_input(new ShaderInput(InternalName::make(id), LVector4f(n1,n2,n3,n4), priority));
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: ShaderAttrib::set_instance_count
+//       Access: Published
+//  Description: Sets the geometry instance count. Do not confuse
+//               this with instanceTo, which is used for animation
+//               instancing, and has nothing to do with this.
+//               A value of 0 means not to use instancing at all.
+////////////////////////////////////////////////////////////////////
+CPT(RenderAttrib) ShaderAttrib::
+set_instance_count(int instance_count) const {
+  ShaderAttrib *result = new ShaderAttrib(*this);
+  result->_instance_count = instance_count;
+  return return_new(result);
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: ShaderAttrib::clear_shader_input
 //       Access: Published
@@ -456,6 +470,9 @@ compare_to_impl(const RenderAttrib *other) const {
   if (this->_has_flags != that->_has_flags) {
     return (this->_has_flags < that->_has_flags) ? -1 : 1;
   }
+  if (this->_instance_count != that->_instance_count) {
+    return (this->_instance_count < that->_instance_count) ? -1 : 1;
+  }
   
   Inputs::const_iterator i1 = this->_inputs.begin();
   Inputs::const_iterator i2 = that->_inputs.begin();
@@ -511,6 +528,8 @@ compose_impl(const RenderAttrib *other) const {
       }
     }
   }
+  // Just copy the instance count.
+  attr->_instance_count = over->_instance_count;
   // Update the flags.
   attr->_flags &= ~(over->_has_flags);
   attr->_flags |= over->_flags;

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

@@ -45,6 +45,7 @@ PUBLISHED:
   INLINE bool               has_shader() const;
   INLINE bool               auto_shader() const;
   INLINE int                get_shader_priority() const;
+  INLINE int                get_instance_count() const;
   
   CPT(RenderAttrib) set_shader(const Shader *s, int priority=0) const;
   CPT(RenderAttrib) set_shader_off(int priority=0) const;
@@ -62,6 +63,8 @@ PUBLISHED:
   CPT(RenderAttrib) set_shader_input(const string &id, double n1=0, double n2=0, double n3=0, double n4=1,
                                      int priority=0) const;
 
+  CPT(RenderAttrib) set_instance_count(int instance_count) const;
+
   CPT(RenderAttrib) set_flag(int flag, bool value) const;
   CPT(RenderAttrib) clear_flag(int flag) const;
 
@@ -96,6 +99,7 @@ private:
   bool        _has_shader;
   int         _flags;
   int         _has_flags;
+  int         _instance_count;
   typedef pmap < CPT(InternalName), CPT(ShaderInput) > Inputs;
   Inputs _inputs;