diff --git a/panda/src/cull/cullBinStateSorted.I b/panda/src/cull/cullBinStateSorted.I index 8f16030d0d..4b9d2bf601 100644 --- a/panda/src/cull/cullBinStateSorted.I +++ b/panda/src/cull/cullBinStateSorted.I @@ -33,7 +33,8 @@ CullBinStateSorted(const string &name, GraphicsStateGuardianBase *gsg, //////////////////////////////////////////////////////////////////// INLINE CullBinStateSorted::ObjectData:: ObjectData(CullableObject *object) : - _object(object) + _object(object), + _format(object->_munged_data->get_format()) { } @@ -45,16 +46,30 @@ ObjectData(CullableObject *object) : //////////////////////////////////////////////////////////////////// INLINE bool CullBinStateSorted::ObjectData:: operator < (const ObjectData &other) const { - // First group objects by transform, since transform changes are - // supposed to be expensive. + // Group by state changes, in approximate order from heaviest + // change to lightest change. + const RenderState *sa = _object->_state; + const RenderState *sb = other._object->_state; + int compare = sa->compare_sort(*sb); + if (compare != 0) { + return compare < 0; + } + + // Vertex format changes are also fairly slow. + if (_format != other._format) { + return _format < other._format; + } + + // Prevent unnecessary vertex buffer rebinds. + if (_object->_munged_data != other._object->_munged_data) { + return _object->_munged_data < other._object->_munged_data; + } + + // Uniform updates are actually pretty fast. if (_object->_internal_transform != other._object->_internal_transform) { return _object->_internal_transform < other._object->_internal_transform; } - // Then group by other state changes, in approximate order from - // heaviest change to lightest change. - const RenderState *sa = _object->_state; - const RenderState *sb = other._object->_state; - return sa->compare_sort(*sb) < 0; + return 0; } diff --git a/panda/src/cull/cullBinStateSorted.h b/panda/src/cull/cullBinStateSorted.h index bcc8de5020..70b0f85803 100644 --- a/panda/src/cull/cullBinStateSorted.h +++ b/panda/src/cull/cullBinStateSorted.h @@ -61,6 +61,7 @@ private: INLINE bool operator < (const ObjectData &other) const; CullableObject *_object; + const GeomVertexFormat *_format; }; typedef pvector Objects; diff --git a/panda/src/glstuff/glCgShaderContext_src.cxx b/panda/src/glstuff/glCgShaderContext_src.cxx index e7193c9cc7..350a40b335 100644 --- a/panda/src/glstuff/glCgShaderContext_src.cxx +++ b/panda/src/glstuff/glCgShaderContext_src.cxx @@ -1036,7 +1036,7 @@ disable_shader_texture_bindings() { if (p == 0) continue; int texunit = cgGetParameterResourceIndex(p); - _glgsg->_glActiveTexture(GL_TEXTURE0 + texunit); + _glgsg->set_active_texture_stage(texunit); glBindTexture(GL_TEXTURE_1D, 0); glBindTexture(GL_TEXTURE_2D, 0); @@ -1099,7 +1099,7 @@ update_shader_texture_bindings(ShaderContext *prev) { if (tex.is_null()) { // Apply a white texture in order to make it easier to use a shader // that takes a texture on a model that doesn't have a texture applied. - _glgsg->_glActiveTexture(GL_TEXTURE0 + i); + _glgsg->set_active_texture_stage(i); _glgsg->apply_white_texture(); continue; } @@ -1115,7 +1115,7 @@ update_shader_texture_bindings(ShaderContext *prev) { continue; } - _glgsg->_glActiveTexture(GL_TEXTURE0 + texunit); + _glgsg->set_active_texture_stage(texunit); TextureContext *tc = tex->prepare_now(view, _glgsg->_prepared_objects, _glgsg); if (tc == (TextureContext*)NULL) { diff --git a/panda/src/glstuff/glGraphicsStateGuardian_src.I b/panda/src/glstuff/glGraphicsStateGuardian_src.I index 8a12c27ccd..2055f56b6c 100644 --- a/panda/src/glstuff/glGraphicsStateGuardian_src.I +++ b/panda/src/glstuff/glGraphicsStateGuardian_src.I @@ -288,6 +288,23 @@ set_vertex_attrib_divisor(GLuint index, GLuint divisor) { #endif +//////////////////////////////////////////////////////////////////// +// Function: GLGraphicsStateGuardian::set_active_texture_stage +// Access: Protected +// Description: Calls glActiveTexture. +//////////////////////////////////////////////////////////////////// +INLINE void CLP(GraphicsStateGuardian):: +set_active_texture_stage(int i) { + if (i != _active_texture_stage) { +#ifdef OPENGLES_2 + glActiveTexture(GL_TEXTURE0 + i); +#else + _glActiveTexture(GL_TEXTURE0 + i); +#endif + _active_texture_stage = i; + } +} + //////////////////////////////////////////////////////////////////// // Function: GLGraphicsStateGuardian::enable_multisample_antialias // Access: Protected diff --git a/panda/src/glstuff/glGraphicsStateGuardian_src.cxx b/panda/src/glstuff/glGraphicsStateGuardian_src.cxx index 265e25c9fe..e87029272a 100644 --- a/panda/src/glstuff/glGraphicsStateGuardian_src.cxx +++ b/panda/src/glstuff/glGraphicsStateGuardian_src.cxx @@ -1086,9 +1086,7 @@ reset() { has_extension("GL_ARB_texture_non_power_of_two"); #endif -#ifdef OPENGLES_2 - _glActiveTexture = glActiveTexture; -#else +#ifndef OPENGLES_2 bool supports_multitexture = false; if (is_at_least_gl_version(1, 3) || is_at_least_gles_version(1, 1)) { supports_multitexture = true; @@ -1641,6 +1639,28 @@ reset() { _glVertexAttribLPointer = NULL; #endif +#ifndef OPENGLES + _use_vertex_attrib_binding = false; + if (is_at_least_gl_version(4, 3) || has_extension("GL_ARB_vertex_attrib_binding")) { + _glBindVertexBuffer = (PFNGLBINDVERTEXBUFFERPROC) + get_extension_func("glBindVertexBuffer"); + _glVertexAttribFormat = (PFNGLVERTEXATTRIBFORMATPROC) + get_extension_func("glVertexAttribFormat"); + _glVertexAttribIFormat = (PFNGLVERTEXATTRIBIFORMATPROC) + get_extension_func("glVertexAttribIFormat"); + _glVertexAttribLFormat = (PFNGLVERTEXATTRIBLFORMATPROC) + get_extension_func("glVertexAttribLFormat"); + _glVertexAttribBinding = (PFNGLVERTEXATTRIBBINDINGPROC) + get_extension_func("glVertexAttribBinding"); + _glVertexBindingDivisor = (PFNGLVERTEXBINDINGDIVISORPROC) + get_extension_func("glVertexBindingDivisor"); + + if (gl_fixed_vertex_attrib_locations) { + _use_vertex_attrib_binding = true; + } + } +#endif + // We need to have a default shader to apply in case // something didn't happen to have a shader applied, or // if it failed to compile. This default shader just outputs @@ -2344,6 +2364,7 @@ reset() { } } + _active_texture_stage = -1; _num_active_texture_stages = 0; // Check availability of anisotropic texture filtering. @@ -2407,18 +2428,22 @@ reset() { // Check availability of multi-bind functions. _supports_multi_bind = false; +#ifndef OPENGLES if (is_at_least_gl_version(4, 4) || has_extension("GL_ARB_multi_bind")) { _glBindTextures = (PFNGLBINDTEXTURESPROC) get_extension_func("glBindTextures"); _glBindImageTextures = (PFNGLBINDIMAGETEXTURESPROC) get_extension_func("glBindImageTextures"); -#ifndef OPENGLES if (_supports_sampler_objects) { _glBindSamplers = (PFNGLBINDSAMPLERSPROC) get_extension_func("glBindSamplers"); } -#endif // OPENGLES + + if (_use_vertex_attrib_binding) { + _glBindVertexBuffers = (PFNGLBINDVERTEXBUFFERSPROC) + get_extension_func("glBindVertexBuffers"); + } if (_glBindTextures != NULL && _glBindImageTextures != NULL) { _supports_multi_bind = true; @@ -2427,6 +2452,7 @@ reset() { << "ARB_multi_bind advertised as supported by OpenGL runtime, but could not get pointers to extension function.\n"; } } +#endif // OPENGLES if (is_at_least_gl_version(4, 3) || has_extension("GL_ARB_internalformat_query2")) { _glGetInternalformativ = (PFNGLGETINTERNALFORMATIVPROC) @@ -2616,6 +2642,12 @@ reset() { _point_size = 1.0f; _point_perspective = false; +#ifndef OPENGLES + _current_vertex_buffers.clear(); + _current_vertex_format.clear(); + memset(_vertex_attrib_columns, 0, sizeof(const GeomVertexColumn *) * 32); +#endif + report_my_gl_errors(); #ifdef SUPPORT_FIXED_FUNCTION @@ -3108,7 +3140,7 @@ clear_before_callback() { // Some callbacks may quite reasonably assume that the active // texture stage is still set to stage 0. CEGUI, in particular, // makes this assumption. - _glActiveTexture(GL_TEXTURE0); + set_active_texture_stage(0); #ifdef SUPPORT_FIXED_FUNCTION _glClientActiveTexture(GL_TEXTURE0); #endif @@ -3375,6 +3407,9 @@ end_frame(Thread *current_thread) { } #endif + // Respecify the active texture next frame, for good measure. + _active_texture_stage = -1; + // Calling glFlush() at the end of the frame is particularly // necessary if this is a single-buffered visual, so that the frame // will be finished drawing before we return to the application. @@ -3636,6 +3671,15 @@ begin_draw_primitives(const GeomPipelineReader *geom_reader, _use_sender = !vertex_arrays; #endif +#ifndef OPENGLES + if (_use_vertex_attrib_binding) { + const GeomVertexFormat *format = data_reader->get_format(); + if (format != _current_vertex_format) { + update_shader_vertex_format(format); + } + } +#endif + { //PStatGPUTimer timer(this, _vertex_array_update_pcollector); #ifdef OPENGLES_1 @@ -3908,42 +3952,97 @@ unbind_buffers() { _current_ibuffer_index = 0; } +#ifndef OPENGLES + if (_current_vertex_buffers.size() > 1 && _supports_multi_bind) { + _glBindVertexBuffers(0, _current_vertex_buffers.size(), NULL, NULL, NULL); + } else { + for (int i = 0; i < _current_vertex_buffers.size(); ++i) { + if (_current_vertex_buffers[i] != 0) { + _glBindVertexBuffer(i, 0, 0, 0); + } + } + } + _current_vertex_buffers.clear(); +#endif + #ifdef SUPPORT_FIXED_FUNCTION disable_standard_vertex_arrays(); #endif } -#ifdef SUPPORT_FIXED_FUNCTION +#ifndef OPENGLES //////////////////////////////////////////////////////////////////// -// Function: GLGraphicsStateGuardian::disable_standard_vertex_arrays +// Function: GLGraphicsStateGuardian::update_shader_vertex_format // Access: Protected -// Description: Used to disable all the standard vertex arrays that -// are currently enabled. glShaderContexts are -// responsible for setting up their own vertex arrays, -// but before they can do so, the standard vertex -// arrays need to be disabled to get them "out of the -// way." Called only from begin_draw_primitives. +// Description: Updates the vertex format used by the shader. This +// is still an experimental feature. //////////////////////////////////////////////////////////////////// void CLP(GraphicsStateGuardian):: -disable_standard_vertex_arrays() { -#ifdef SUPPORT_IMMEDIATE_MODE - if (_use_sender) return; -#endif +update_shader_vertex_format(const GeomVertexFormat *format) { + size_t num_columns = format->get_num_columns(); + for (size_t ci = 0; ci < num_columns; ++ci) { + GLuint binding = format->get_array_with(ci); + const GeomVertexColumn *column = format->get_column(ci); - glDisableClientState(GL_NORMAL_ARRAY); - glDisableClientState(GL_COLOR_ARRAY); - GLPf(Color4)(1.0f, 1.0f, 1.0f, 1.0f); + // Needs improvement, obviously. + const InternalName *name = column->get_name(); + GLuint loc; + if (name == InternalName::get_vertex()) { + loc = 0; + } else if (name == InternalName::get_transform_weight()) { + loc = 1; + } else if (name == InternalName::get_normal()) { + loc = 2; + } else if (name == InternalName::get_color()) { + loc = 3; + } else if (name == InternalName::get_transform_index()) { + loc = 7; + } else if (name == InternalName::get_texcoord()) { + loc = 8; + } else { + // Not yet supported, ignore for now. This system will be improved. + continue; + } - for (int stage_index=0; stage_index < _last_max_stage_index; stage_index++) { - _glClientActiveTexture(GL_TEXTURE0 + stage_index); - glDisableClientState(GL_TEXTURE_COORD_ARRAY); + if (_vertex_attrib_columns[loc] != NULL && + _vertex_attrib_columns[loc]->compare_to(*column) == 0) { + continue; + } + _vertex_attrib_columns[loc] = column; + + GLuint offset = column->get_start(); + GLenum type = get_numeric_type(column->get_numeric_type()); + GLboolean normalized = (column->get_contents() == GeomEnums::C_color); + GLint size = column->get_num_values(); + + if (column->get_numeric_type() == GeomEnums::NT_packed_dabc) { + // GL_BGRA is a special accepted value available since OpenGL 3.2. + // It requires us to pass GL_TRUE for normalized. + size = GL_BGRA; + normalized = GL_TRUE; + } + + for (int i = 0; i < column->get_num_elements(); ++i) { + if (loc == 7) { // Temp hack + _glVertexAttribIFormat(loc, size, type, offset); + } else { + _glVertexAttribFormat(loc, size, type, normalized, offset); + } + _glVertexAttribBinding(loc, binding); + + offset += column->get_element_stride(); + ++loc; + } } - _last_max_stage_index = 0; - glDisableClientState(GL_VERTEX_ARRAY); - report_my_gl_errors(); + size_t num_arrays = format->get_num_arrays(); + for (size_t ai = 0; ai < num_arrays; ++ai) { + _glVertexBindingDivisor(ai, format->get_array(ai)->get_divisor()); + } + + _current_vertex_format = format; } -#endif // SUPPORT_FIXED_FUNCTION +#endif //////////////////////////////////////////////////////////////////// // Function: GLGraphicsStateGuardian::draw_triangles @@ -5112,7 +5211,7 @@ prepare_vertex_buffer(GeomVertexArrayData *data) { } report_my_gl_errors(); - apply_vertex_buffer(gvbc, data->get_handle(), false); + update_vertex_buffer(gvbc, data->get_handle(), false); return gvbc; } @@ -5120,31 +5219,22 @@ prepare_vertex_buffer(GeomVertexArrayData *data) { } //////////////////////////////////////////////////////////////////// -// Function: GLGraphicsStateGuardian::apply_vertex_buffer +// Function: GLGraphicsStateGuardian::update_vertex_buffer // Access: Public -// Description: Makes the data the currently available data for -// rendering. +// Description: Makes sure that the data in the vertex buffer is +// up-to-date. This may bind it to the GL_ARRAY_BUFFER +// binding point if necessary. //////////////////////////////////////////////////////////////////// bool CLP(GraphicsStateGuardian):: -apply_vertex_buffer(VertexBufferContext *vbc, - const GeomVertexArrayDataHandle *reader, bool force) { +update_vertex_buffer(CLP(VertexBufferContext) *gvbc, + const GeomVertexArrayDataHandle *reader, bool force) { nassertr(_supports_buffers, false); if (reader->get_modified() == UpdateSeq::initial()) { // No need to re-apply. return true; } - CLP(VertexBufferContext) *gvbc = DCAST(CLP(VertexBufferContext), vbc); - - if (_current_vbuffer_index != gvbc->_index) { - if (GLCAT.is_spam() && gl_debug_buffers) { - GLCAT.spam() - << "binding vertex buffer " << (int)gvbc->_index << "\n"; - } - _glBindBuffer(GL_ARRAY_BUFFER, gvbc->_index); - _current_vbuffer_index = gvbc->_index; - gvbc->set_active(true); - } + gvbc->set_active(true); if (gvbc->was_modified(reader)) { int num_bytes = reader->get_data_size_bytes(); @@ -5160,6 +5250,15 @@ apply_vertex_buffer(VertexBufferContext *vbc, } PStatGPUTimer timer(this, _load_vertex_buffer_pcollector, reader->get_current_thread()); + if (_current_vbuffer_index != gvbc->_index) { + if (GLCAT.is_spam() && gl_debug_buffers) { + GLCAT.spam() + << "binding vertex buffer " << (int)gvbc->_index << "\n"; + } + _glBindBuffer(GL_ARRAY_BUFFER, gvbc->_index); + _current_vbuffer_index = gvbc->_index; + } + if (gvbc->changed_size(reader) || gvbc->changed_usage_hint(reader)) { _glBufferData(GL_ARRAY_BUFFER, num_bytes, client_pointer, get_usage(reader->get_usage_hint())); @@ -5263,12 +5362,23 @@ setup_array_data(const unsigned char *&client_pointer, } // Prepare the buffer object and bind it. - VertexBufferContext *vbc = array_reader->prepare_now(get_prepared_objects(), this); - nassertr(vbc != (VertexBufferContext *)NULL, false); - if (!apply_vertex_buffer(vbc, array_reader, force)) { + CLP(VertexBufferContext) *gvbc = DCAST(CLP(VertexBufferContext), + array_reader->prepare_now(get_prepared_objects(), this)); + + nassertr(gvbc != (CLP(VertexBufferContext) *)NULL, false); + if (!update_vertex_buffer(gvbc, array_reader, force)) { return false; } + if (_current_vbuffer_index != gvbc->_index) { + if (GLCAT.is_spam() && gl_debug_buffers) { + GLCAT.spam() + << "binding vertex buffer " << (int)gvbc->_index << "\n"; + } + _glBindBuffer(GL_ARRAY_BUFFER, gvbc->_index); + _current_vbuffer_index = gvbc->_index; + } + // NULL is the OpenGL convention for the first byte of the buffer object. client_pointer = NULL; return true; @@ -9316,6 +9426,14 @@ void CLP(GraphicsStateGuardian):: reissue_transforms() { prepare_lens(); do_issue_transform(); + + _active_texture_stage = -1; + +#ifndef OPENGLES + // Might also want to reissue the vertex format, for good measure. + _current_vertex_format.clear(); + memset(_vertex_attrib_columns, 0, sizeof(const GeomVertexColumn *) * 32); +#endif } #ifdef SUPPORT_FIXED_FUNCTION @@ -9592,7 +9710,7 @@ set_state_and_transform(const RenderState *target, _state_mask.clear_bit(TextureAttrib::get_class_slot()); } #ifndef SUPPORT_FIXED_FUNCTION - else { // In the case of OpenGL ES 2.x, we need to glUseShader before we draw anything. + else if (_current_shader == NULL) { // In the case of OpenGL ES 2.x, we need to glUseShader before we draw anything. do_issue_shader(); _state_mask.clear_bit(TextureAttrib::get_class_slot()); } @@ -9945,7 +10063,7 @@ update_standard_texture_bindings() { nassertv(texture != (Texture *)NULL); // Issue the texture on stage i. - _glActiveTexture(GL_TEXTURE0 + i); + set_active_texture_stage(i); // First, turn off the previous texture mode. glDisable(GL_TEXTURE_2D); @@ -10114,7 +10232,7 @@ update_standard_texture_bindings() { // Disable the texture stages that are no longer used. for (i = num_stages; i < _num_active_texture_stages; i++) { - _glActiveTexture(GL_TEXTURE0 + i); + set_active_texture_stage(i); glDisable(GL_TEXTURE_2D); if (_supports_cube_map) { glDisable(GL_TEXTURE_CUBE_MAP); @@ -10217,7 +10335,7 @@ update_show_usage_texture_bindings(int show_stage_index) { #ifdef SUPPORT_FIXED_FUNCTION // Disable all texture stages. for (i = 0; i < _num_active_texture_stages; i++) { - _glActiveTexture(GL_TEXTURE0 + i); + set_active_texture_stage(i); #ifndef OPENGLES glDisable(GL_TEXTURE_1D); #endif // OPENGLES @@ -10249,7 +10367,7 @@ update_show_usage_texture_bindings(int show_stage_index) { nassertv(texture != (Texture *)NULL); // Choose the corresponding usage texture and apply it. - _glActiveTexture(GL_TEXTURE0 + i); + set_active_texture_stage(i); #ifdef SUPPORT_FIXED_FUNCTION glEnable(GL_TEXTURE_2D); #endif @@ -10366,7 +10484,7 @@ void CLP(GraphicsStateGuardian):: disable_standard_texture_bindings() { // Disable the texture stages that are no longer used. for (int i = 0; i < _num_active_texture_stages; i++) { - _glActiveTexture(GL_TEXTURE0 + i); + set_active_texture_stage(i); #ifndef OPENGLES glDisable(GL_TEXTURE_1D); #endif // OPENGLES @@ -10399,7 +10517,7 @@ do_issue_tex_matrix() { for (int i = 0; i < _num_active_texture_stages; i++) { TextureStage *stage = _target_texture->get_on_ff_stage(i); - _glActiveTexture(GL_TEXTURE0 + i); + set_active_texture_stage(i); glMatrixMode(GL_TEXTURE); @@ -10452,7 +10570,7 @@ do_issue_tex_gen() { for (int i = 0; i < _num_active_texture_stages; i++) { TextureStage *stage = _target_texture->get_on_ff_stage(i); - _glActiveTexture(GL_TEXTURE0 + i); + set_active_texture_stage(i); if (_supports_point_sprite) { #ifdef OPENGLES glTexEnvi(GL_POINT_SPRITE_OES, GL_COORD_REPLACE_OES, GL_FALSE); @@ -10909,7 +11027,7 @@ apply_sampler(GLuint unit, const SamplerState &sampler, CLP(TextureContext) *gtc // We don't support sampler objects. We'll have to bind the // texture and change the texture parameters if they don't match. if (gtc->_active_sampler != sampler) { - _glActiveTexture(GL_TEXTURE0 + unit); + set_active_texture_stage(unit); apply_texture(gtc); specify_texture(gtc, sampler); } diff --git a/panda/src/glstuff/glGraphicsStateGuardian_src.h b/panda/src/glstuff/glGraphicsStateGuardian_src.h index fb25b754e5..76fec0f546 100644 --- a/panda/src/glstuff/glGraphicsStateGuardian_src.h +++ b/panda/src/glstuff/glGraphicsStateGuardian_src.h @@ -306,9 +306,9 @@ public: void record_deleted_display_list(GLuint index); virtual VertexBufferContext *prepare_vertex_buffer(GeomVertexArrayData *data); - bool apply_vertex_buffer(VertexBufferContext *vbc, - const GeomVertexArrayDataHandle *reader, - bool force); + bool update_vertex_buffer(CLP(VertexBufferContext) *gvbc, + const GeomVertexArrayDataHandle *reader, + bool force); virtual void release_vertex_buffer(VertexBufferContext *vbc); bool setup_array_data(const unsigned char *&client_pointer, @@ -457,6 +457,8 @@ protected: INLINE void set_vertex_attrib_divisor(GLuint index, GLuint divisor); #endif + INLINE void set_active_texture_stage(int i); + INLINE void enable_multisample_antialias(bool val); INLINE void enable_multisample_alpha_one(bool val); INLINE void enable_multisample_alpha_mask(bool val); @@ -533,6 +535,9 @@ protected: void disable_standard_texture_bindings(); void update_standard_texture_bindings(); #endif +#ifndef OPENGLES + void update_shader_vertex_format(const GeomVertexFormat *format); +#endif void apply_white_texture(); GLuint get_white_texture(); @@ -651,6 +656,15 @@ protected: GLuint _current_vbuffer_index; GLuint _current_ibuffer_index; GLuint _current_fbo; + +#ifndef OPENGLES + pvector _current_vertex_buffers; + bool _use_vertex_attrib_binding; + CPT(GeomVertexFormat) _current_vertex_format; + const GeomVertexColumn *_vertex_attrib_columns[32]; +#endif + + int _active_texture_stage; int _num_active_texture_stages; PN_stdfloat _max_anisotropy; bool _supports_anisotropy; @@ -735,7 +749,9 @@ public: #endif #endif +#ifndef OPENGLES_2 PFNGLACTIVETEXTUREPROC _glActiveTexture; +#endif #ifdef SUPPORT_FIXED_FUNCTION PFNGLCLIENTACTIVETEXTUREPROC _glClientActiveTexture; #endif @@ -897,6 +913,13 @@ public: PFNGLDRAWELEMENTSINSTANCEDPROC _glDrawElementsInstanced; #endif // !OPENGLES_1 #ifndef OPENGLES + PFNGLBINDVERTEXBUFFERPROC _glBindVertexBuffer; + PFNGLBINDVERTEXBUFFERSPROC _glBindVertexBuffers; + PFNGLVERTEXATTRIBFORMATPROC _glVertexAttribFormat; + PFNGLVERTEXATTRIBIFORMATPROC _glVertexAttribIFormat; + PFNGLVERTEXATTRIBLFORMATPROC _glVertexAttribLFormat; + PFNGLVERTEXATTRIBBINDINGPROC _glVertexAttribBinding; + PFNGLVERTEXBINDINGDIVISORPROC _glVertexBindingDivisor; PFNGLGETACTIVEUNIFORMSIVPROC _glGetActiveUniformsiv; PFNGLGETACTIVEUNIFORMBLOCKIVPROC _glGetActiveUniformBlockiv; PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC _glGetActiveUniformBlockName; diff --git a/panda/src/glstuff/glShaderContext_src.cxx b/panda/src/glstuff/glShaderContext_src.cxx index 218c26260d..a3cafd7d9b 100644 --- a/panda/src/glstuff/glShaderContext_src.cxx +++ b/panda/src/glstuff/glShaderContext_src.cxx @@ -262,6 +262,7 @@ CLP(ShaderContext)(CLP(GraphicsStateGuardian) *glgsg, Shader *s) : ShaderContext _glgsg = glgsg; _glsl_program = 0; _uses_standard_vertex_arrays = false; + _enabled_attribs.clear(); _color_attrib_index = -1; _transform_table_index = -1; _slider_table_index = -1; @@ -279,13 +280,31 @@ CLP(ShaderContext)(CLP(GraphicsStateGuardian) *glgsg, Shader *s) : ShaderContext return; } - // Create a buffer the size of the longest uniform name. Note - // that Intel HD drivers report values that are too low. + // Process the vertex attributes first. + GLint param_count = 0; GLint name_buflen = 0; - _glgsg->_glGetProgramiv(_glsl_program, GL_ACTIVE_UNIFORM_MAX_LENGTH, &name_buflen); + _glgsg->_glGetProgramiv(_glsl_program, GL_ACTIVE_ATTRIBUTES, ¶m_count); + _glgsg->_glGetProgramiv(_glsl_program, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, &name_buflen); name_buflen = max(64, name_buflen); char *name_buffer = (char *)alloca(name_buflen); + _shader->_var_spec.clear(); + for (int i = 0; i < param_count; ++i) { + reflect_attribute(i, name_buffer, name_buflen); + } + + /*if (gl_fixed_vertex_attrib_locations) { + // Relink the shader for glBindAttribLocation to take effect. + _glgsg->_glLinkProgram(_glsl_program); + }*/ + + // Create a buffer the size of the longest uniform name. Note + // that Intel HD drivers report values that are too low. + name_buflen = 0; + _glgsg->_glGetProgramiv(_glsl_program, GL_ACTIVE_UNIFORM_MAX_LENGTH, &name_buflen); + name_buflen = max(64, name_buflen); + name_buffer = (char *)alloca(name_buflen); + #ifndef OPENGLES // Get the used uniform blocks. if (_glgsg->_supports_uniform_buffers) { @@ -316,7 +335,7 @@ CLP(ShaderContext)(CLP(GraphicsStateGuardian) *glgsg, Shader *s) : ShaderContext _glgsg->_glUseProgram(_glsl_program); // Analyze the uniforms. - GLint param_count = 0; + param_count = 0; _glgsg->_glGetProgramiv(_glsl_program, GL_ACTIVE_UNIFORMS, ¶m_count); _shader->_ptr_spec.clear(); @@ -326,17 +345,6 @@ CLP(ShaderContext)(CLP(GraphicsStateGuardian) *glgsg, Shader *s) : ShaderContext reflect_uniform(i, name_buffer, name_buflen); } - // Now we've processed the uniforms, we'll process the attribs. - _glgsg->_glGetProgramiv(_glsl_program, GL_ACTIVE_ATTRIBUTES, ¶m_count); - _glgsg->_glGetProgramiv(_glsl_program, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, &name_buflen); - name_buflen = max(64, name_buflen); - name_buffer = (char *)alloca(name_buflen); - - _shader->_var_spec.clear(); - for (int i = 0; i < param_count; ++i) { - reflect_attribute(i, name_buffer, name_buflen); - } - _glgsg->report_my_gl_errors(); // Restore the active shader. @@ -470,6 +478,37 @@ reflect_attribute(int i, char *name_buffer, GLsizei name_buflen) { break; } + // Experimental feature. + if (gl_fixed_vertex_attrib_locations) { + GLint loc; + if (bind._name == InternalName::get_vertex()) { + loc = 0; + } else if (bind._name == InternalName::get_transform_weight()) { + loc = 1; + } else if (bind._name == InternalName::get_normal()) { + loc = 2; + } else if (bind._name == InternalName::get_color()) { + loc = 3; + } else if (bind._name == InternalName::get_transform_index()) { + loc = 7; + } else if (bind._name == InternalName::get_texcoord() && + bind._append_uv >= 0 && bind._append_uv < 8) { + loc = 8 + bind._append_uv; + } else { + GLCAT.error() + << "Vertex attrib '" << name_buffer << "' not yet supported with " + "gl-fixed-vertex-attrib-locations!\n"; + return; + } + if (loc != p) { + GLCAT.error() + << "Vertex attrib '" << name_buffer << "' was bound to the wrong slot!\n"; + return; + } + //_glgsg->_glBindAttribLocation(_glsl_program, loc, name_buffer); + _enabled_attribs.set_range(loc, bind._elements); + } + _shader->_var_spec.push_back(bind); } @@ -2045,91 +2084,152 @@ update_shader_vertex_arrays(ShaderContext *prev, bool force) { _state_rs->get_attrib_def(color_attrib); const GeomVertexArrayDataHandle *array_reader; - Geom::NumericType numeric_type; - int start, stride, num_values; - size_t nvarying = _shader->_var_spec.size(); - - GLuint max_p = 0; - - for (size_t i = 0; i < nvarying; ++i) { - const Shader::ShaderVarSpec &bind = _shader->_var_spec[i]; - InternalName *name = bind._name; - int texslot = bind._append_uv; - - if (texslot >= 0 && texslot < _glgsg->_state_texture->get_num_on_stages()) { - TextureStage *stage = _glgsg->_state_texture->get_on_stage(texslot); - InternalName *texname = stage->get_texcoord_name(); - - if (name == InternalName::get_texcoord()) { - name = texname; - } else if (texname != InternalName::get_texcoord()) { - name = name->append(texname->get_basename()); - } - } - - GLuint p = bind._id._seqno; - max_p = max(max_p, p + 1); - - // Don't apply vertex colors if they are disabled with a ColorAttrib. - int num_elements, element_stride, divisor; - bool normalized; - if ((p != _color_attrib_index || color_attrib->get_type() == ColorAttrib::T_vertex) && - _glgsg->_data_reader->get_array_info(name, array_reader, - num_values, numeric_type, - normalized, start, stride, divisor, - num_elements, element_stride)) { - const unsigned char *client_pointer; - if (!_glgsg->setup_array_data(client_pointer, array_reader, force)) { - return false; - } - client_pointer += start; - - for (int i = 0; i < num_elements; ++i) { - _glgsg->enable_vertex_attrib_array(p); #ifndef OPENGLES - if (bind._integer) { - _glgsg->_glVertexAttribIPointer(p, num_values, _glgsg->get_numeric_type(numeric_type), - stride, client_pointer); - } else -#endif - if (numeric_type == GeomEnums::NT_packed_dabc) { - // GL_BGRA is a special accepted value available since OpenGL 3.2. - // It requires us to pass GL_TRUE for normalized. - _glgsg->_glVertexAttribPointer(p, GL_BGRA, GL_UNSIGNED_BYTE, - GL_TRUE, stride, client_pointer); - } else { - _glgsg->_glVertexAttribPointer(p, num_values, - _glgsg->get_numeric_type(numeric_type), - normalized, stride, client_pointer); - } + if (_glgsg->_use_vertex_attrib_binding) { + // Use experimental new separated format/binding state. + const GeomVertexDataPipelineReader *data_reader = _glgsg->_data_reader; - if (divisor > 0) { - _glgsg->set_vertex_attrib_divisor(p, divisor); - } + for (int ai = 0; ai < data_reader->get_num_arrays(); ++ai) { + array_reader = data_reader->get_array_reader(ai); - ++p; - client_pointer += element_stride; + // Make sure the vertex buffer is up-to-date. + CLP(VertexBufferContext) *gvbc = DCAST(CLP(VertexBufferContext), + array_reader->prepare_now(_glgsg->get_prepared_objects(), _glgsg)); + nassertr(gvbc != (CLP(VertexBufferContext) *)NULL, false); + + if (!_glgsg->update_vertex_buffer(gvbc, array_reader, force)) { + return false; } - } else { - for (int i = 0; i < bind._elements; ++i) { - _glgsg->disable_vertex_attrib_array(p + i); + + GLintptr stride = array_reader->get_array_format()->get_stride(); + + // Bind the vertex buffer to the binding index. + if (ai >= _glgsg->_current_vertex_buffers.size()) { + _glgsg->_current_vertex_buffers.resize(ai + 1, 0); } - if (p == _color_attrib_index) { - // Vertex colors are disabled or not present. Apply flat color. -#ifdef STDFLOAT_DOUBLE - _glgsg->_glVertexAttrib4dv(p, color_attrib->get_color().get_data()); -#else - _glgsg->_glVertexAttrib4fv(p, color_attrib->get_color().get_data()); -#endif + if (_glgsg->_current_vertex_buffers[ai] != gvbc->_index) { + _glgsg->_glBindVertexBuffer(ai, gvbc->_index, 0, stride); + _glgsg->_current_vertex_buffers[ai] = gvbc->_index; } } - } - // Disable attribute arrays we don't use. - GLint highest_p = _glgsg->_enabled_vertex_attrib_arrays.get_highest_on_bit() + 1; - for (GLint p = max_p; p < highest_p; ++p) { - _glgsg->disable_vertex_attrib_array(p); + // Figure out which attributes to enable or disable. + BitMask32 enabled_attribs = _enabled_attribs; + if (_color_attrib_index != -1 && + color_attrib->get_type() != ColorAttrib::T_vertex) { + // Vertex colours are disabled. + enabled_attribs.clear_bit(_color_attrib_index); + +#ifdef STDFLOAT_DOUBLE + _glgsg->_glVertexAttrib4dv(_color_attrib_index, color_attrib->get_color().get_data()); +#else + _glgsg->_glVertexAttrib4fv(_color_attrib_index, color_attrib->get_color().get_data()); +#endif + } + + BitMask32 changed_attribs = enabled_attribs ^ _glgsg->_enabled_vertex_attrib_arrays; + + for (int i = 0; i < 32; ++i) { + if (changed_attribs.get_bit(i)) { + if (enabled_attribs.get_bit(i)) { + _glgsg->_glEnableVertexAttribArray(i); + } else { + _glgsg->_glDisableVertexAttribArray(i); + } + } + } + _glgsg->_enabled_vertex_attrib_arrays = enabled_attribs; + + } else +#endif + { + Geom::NumericType numeric_type; + int start, stride, num_values; + size_t nvarying = _shader->_var_spec.size(); + + GLuint max_p = 0; + + for (size_t i = 0; i < nvarying; ++i) { + const Shader::ShaderVarSpec &bind = _shader->_var_spec[i]; + InternalName *name = bind._name; + int texslot = bind._append_uv; + + if (texslot >= 0 && texslot < _glgsg->_state_texture->get_num_on_stages()) { + TextureStage *stage = _glgsg->_state_texture->get_on_stage(texslot); + InternalName *texname = stage->get_texcoord_name(); + + if (name == InternalName::get_texcoord()) { + name = texname; + } else if (texname != InternalName::get_texcoord()) { + name = name->append(texname->get_basename()); + } + } + + GLuint p = bind._id._seqno; + max_p = max(max_p, p + 1); + + // Don't apply vertex colors if they are disabled with a ColorAttrib. + int num_elements, element_stride, divisor; + bool normalized; + if ((p != _color_attrib_index || color_attrib->get_type() == ColorAttrib::T_vertex) && + _glgsg->_data_reader->get_array_info(name, array_reader, + num_values, numeric_type, + normalized, start, stride, divisor, + num_elements, element_stride)) { + const unsigned char *client_pointer; + if (!_glgsg->setup_array_data(client_pointer, array_reader, force)) { + return false; + } + client_pointer += start; + + for (int i = 0; i < num_elements; ++i) { + _glgsg->enable_vertex_attrib_array(p); + +#ifndef OPENGLES + if (bind._integer) { + _glgsg->_glVertexAttribIPointer(p, num_values, _glgsg->get_numeric_type(numeric_type), + stride, client_pointer); + } else +#endif + if (numeric_type == GeomEnums::NT_packed_dabc) { + // GL_BGRA is a special accepted value available since OpenGL 3.2. + // It requires us to pass GL_TRUE for normalized. + _glgsg->_glVertexAttribPointer(p, GL_BGRA, GL_UNSIGNED_BYTE, + GL_TRUE, stride, client_pointer); + } else { + _glgsg->_glVertexAttribPointer(p, num_values, + _glgsg->get_numeric_type(numeric_type), + normalized, stride, client_pointer); + } + + if (divisor > 0) { + _glgsg->set_vertex_attrib_divisor(p, divisor); + } + + ++p; + client_pointer += element_stride; + } + } else { + for (int i = 0; i < bind._elements; ++i) { + _glgsg->disable_vertex_attrib_array(p + i); + } + if (p == _color_attrib_index) { + // Vertex colors are disabled or not present. Apply flat color. +#ifdef STDFLOAT_DOUBLE + _glgsg->_glVertexAttrib4dv(p, color_attrib->get_color().get_data()); +#else + _glgsg->_glVertexAttrib4fv(p, color_attrib->get_color().get_data()); +#endif + } + } + } + + // Disable attribute arrays we don't use. + GLint highest_p = _glgsg->_enabled_vertex_attrib_arrays.get_highest_on_bit() + 1; + for (GLint p = max_p; p < highest_p; ++p) { + _glgsg->disable_vertex_attrib_array(p); + } } if (_transform_table_index >= 0) { @@ -2180,7 +2280,7 @@ disable_shader_texture_bindings() { } #endif - _glgsg->_glActiveTexture(GL_TEXTURE0 + i); + _glgsg->set_active_texture_stage(i); switch (_shader->_tex_spec[i]._desired_type) { case Texture::TT_1d_texture: @@ -2371,7 +2471,8 @@ update_shader_texture_bindings(ShaderContext *prev) { static const bool multi_bind = false; #else bool multi_bind = false; - if (_glgsg->_supports_multi_bind && _glgsg->_supports_sampler_objects) { + if (num_textures > 1 && + _glgsg->_supports_multi_bind && _glgsg->_supports_sampler_objects) { // Prepare to multi-bind the textures and samplers. multi_bind = true; textures = (GLuint *)alloca(sizeof(GLuint) * num_textures); @@ -2394,7 +2495,7 @@ update_shader_texture_bindings(ShaderContext *prev) { textures[i] = _glgsg->get_white_texture(); samplers[i] = 0; } else { - _glgsg->_glActiveTexture(GL_TEXTURE0 + i); + _glgsg->set_active_texture_stage(i); _glgsg->apply_white_texture(); } continue; @@ -2487,7 +2588,7 @@ update_shader_texture_bindings(ShaderContext *prev) { #endif // !OPENGLES { // Non-multibind case. - _glgsg->_glActiveTexture(GL_TEXTURE0 + i); + _glgsg->set_active_texture_stage(i); if (!_glgsg->update_texture(gtc, false)) { continue; } @@ -2765,6 +2866,15 @@ glsl_compile_and_link() { _glgsg->_glBindAttribLocation(_glsl_program, 2, "p3d_Normal"); _glgsg->_glBindAttribLocation(_glsl_program, 3, "p3d_Color"); + if (gl_fixed_vertex_attrib_locations) { + _glgsg->_glBindAttribLocation(_glsl_program, 1, "transform_weight"); + _glgsg->_glBindAttribLocation(_glsl_program, 2, "normal"); + _glgsg->_glBindAttribLocation(_glsl_program, 3, "color"); + _glgsg->_glBindAttribLocation(_glsl_program, 7, "transform_index"); + _glgsg->_glBindAttribLocation(_glsl_program, 8, "p3d_MultiTexCoord0"); + _glgsg->_glBindAttribLocation(_glsl_program, 8, "texcoord"); + } + // If we requested to retrieve the shader, we should indicate that before linking. #if !defined(NDEBUG) && !defined(OPENGLES) if (gl_dump_compiled_shaders && _glgsg->_supports_get_program_binary) { diff --git a/panda/src/glstuff/glShaderContext_src.h b/panda/src/glstuff/glShaderContext_src.h index 30464ce83d..9f51dd5973 100644 --- a/panda/src/glstuff/glShaderContext_src.h +++ b/panda/src/glstuff/glShaderContext_src.h @@ -83,6 +83,7 @@ private: //typedef pvector ParamContexts; //ParamContexts _params; + BitMask32 _enabled_attribs; GLint _color_attrib_index; GLint _transform_table_index; GLint _slider_table_index; diff --git a/panda/src/glstuff/glmisc_src.cxx b/panda/src/glstuff/glmisc_src.cxx index 44a00fdef2..a76c4b97ba 100644 --- a/panda/src/glstuff/glmisc_src.cxx +++ b/panda/src/glstuff/glmisc_src.cxx @@ -276,6 +276,10 @@ ConfigVariableBool gl_vertex_array_objects "and vertex-buffers are both set. This should usually be " "true unless you suspect a bug in the implementation. ")); +ConfigVariableBool gl_fixed_vertex_attrib_locations + ("gl-fixed-vertex-attrib-locations", false, + PRC_DESC("Experimental feature.")); + ConfigVariableBool gl_support_primitive_restart_index ("gl-support-primitive-restart-index", true, PRC_DESC("Setting this causes Panda to make use of primitive " diff --git a/panda/src/glstuff/glmisc_src.h b/panda/src/glstuff/glmisc_src.h index 204db5882c..ec1db27cf1 100644 --- a/panda/src/glstuff/glmisc_src.h +++ b/panda/src/glstuff/glmisc_src.h @@ -77,6 +77,7 @@ extern ConfigVariableBool gl_immutable_texture_storage; extern ConfigVariableBool gl_use_bindless_texture; extern ConfigVariableBool gl_enable_memory_barriers; extern ConfigVariableBool gl_vertex_array_objects; +extern ConfigVariableBool gl_fixed_vertex_attrib_locations; extern ConfigVariableBool gl_support_primitive_restart_index; extern ConfigVariableBool gl_support_sampler_objects; extern ConfigVariableBool gl_support_shadow_filter; diff --git a/panda/src/gobj/geomVertexColumn.cxx b/panda/src/gobj/geomVertexColumn.cxx index b803f3e1ac..fbec26d4d8 100644 --- a/panda/src/gobj/geomVertexColumn.cxx +++ b/panda/src/gobj/geomVertexColumn.cxx @@ -427,6 +427,13 @@ complete_pointers(TypedWritable **p_list, BamReader *manager) { _name = DCAST(InternalName, p_list[pi++]); + // Make sure that old .bam files are corrected to have C_normal + // normal columns rather than C_vector. + if (manager->get_file_minor_ver() < 38 && + _name == InternalName::get_normal() && _contents == C_vector) { + _contents = C_normal; + } + return pi; } diff --git a/panda/src/grutil/cardMaker.cxx b/panda/src/grutil/cardMaker.cxx index ef759227bc..ae938e6aa4 100644 --- a/panda/src/grutil/cardMaker.cxx +++ b/panda/src/grutil/cardMaker.cxx @@ -64,7 +64,7 @@ generate() { (InternalName::get_vertex(), 3, GeomEnums::NT_stdfloat, GeomEnums::C_point, InternalName::get_normal(), 3, - GeomEnums::NT_stdfloat, GeomEnums::C_vector, + GeomEnums::NT_stdfloat, GeomEnums::C_normal, InternalName::get_texcoord(), 3, GeomEnums::NT_stdfloat, GeomEnums::C_texcoord)); } else {