Reduce redundant state changes, add gl-fixed-vertex-attrib-locations

This commit is contained in:
rdb 2015-12-22 20:22:24 +01:00
parent 927a5bd48b
commit 08c29313b2
12 changed files with 464 additions and 167 deletions

View File

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

View File

@ -61,6 +61,7 @@ private:
INLINE bool operator < (const ObjectData &other) const;
CullableObject *_object;
const GeomVertexFormat *_format;
};
typedef pvector<ObjectData> Objects;

View File

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

View File

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

View File

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

View File

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

View File

@ -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, &param_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, &param_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, &param_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) {

View File

@ -83,6 +83,7 @@ private:
//typedef pvector<ParamContext> ParamContexts;
//ParamContexts _params;
BitMask32 _enabled_attribs;
GLint _color_attrib_index;
GLint _transform_table_index;
GLint _slider_table_index;

View File

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

View File

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

View File

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

View File

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