Add hardware instancing support

This commit is contained in:
rdb 2009-12-21 14:22:12 +00:00
parent f2a937c79f
commit c136904fa9
10 changed files with 285 additions and 52 deletions

View File

@ -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

View File

@ -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;

View File

@ -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;

View File

@ -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.

View File

@ -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;

View File

@ -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

View File

@ -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();

View File

@ -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

View File

@ -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;

View File

@ -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;