From 736f1f70ab8a7995041a707d28d0e1a5f2940f4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Ol=C5=A1=C3=A1k?= Date: Sun, 27 Sep 2020 12:36:13 -0400 Subject: [PATCH] mesa: skip redundant uniform updates for glUniform Viewperf does a lot of redundant uniform updates - 60-80% in some tests. Those are sometimes the only state changes between draw calls. This improves performance by 33% in one viewperf subtest. If you are worried about CPU overhead in the non-redundant case, glthread is the solution. Reviewed-by: Pierre-Eric Pelloux-Prayer Part-of: --- src/mesa/main/uniform_query.cpp | 130 +++++++++++++++++++++++++------- 1 file changed, 101 insertions(+), 29 deletions(-) diff --git a/src/mesa/main/uniform_query.cpp b/src/mesa/main/uniform_query.cpp index f97e7bdaa33..f4c932ec1ba 100644 --- a/src/mesa/main/uniform_query.cpp +++ b/src/mesa/main/uniform_query.cpp @@ -1059,40 +1059,106 @@ _mesa_flush_vertices_for_uniforms(struct gl_context *ctx, ctx->NewDriverState |= new_driver_state; } -static void +static bool copy_uniforms_to_storage(gl_constant_value *storage, struct gl_uniform_storage *uni, struct gl_context *ctx, GLsizei count, const GLvoid *values, const int size_mul, const unsigned offset, const unsigned components, - enum glsl_base_type basicType) + enum glsl_base_type basicType, bool flush) { + const gl_constant_value *src = (const gl_constant_value*)values; bool copy_as_uint64 = uni->is_bindless && (uni->type->is_sampler() || uni->type->is_image()); + if (!uni->type->is_boolean() && !copy_as_uint64) { - memcpy(storage, values, - sizeof(storage[0]) * components * count * size_mul); + unsigned size = sizeof(storage[0]) * components * count * size_mul; + + if (!memcmp(storage, values, size)) + return false; + + if (flush) + _mesa_flush_vertices_for_uniforms(ctx, uni); + + memcpy(storage, values, size); + return true; } else if (copy_as_uint64) { - const union gl_constant_value *src = - (const union gl_constant_value *) values; - GLuint64 *dst = (GLuint64 *)&storage->i; const unsigned elems = components * count; + uint64_t *dst = (uint64_t*)storage; + unsigned i = 0; - for (unsigned i = 0; i < elems; i++) { - dst[i] = src[i].i; - } - } else { - const union gl_constant_value *src = - (const union gl_constant_value *) values; - union gl_constant_value *dst = storage; - const unsigned elems = components * count; - - for (unsigned i = 0; i < elems; i++) { - if (basicType == GLSL_TYPE_FLOAT) { - dst[i].i = src[i].f != 0.0f ? ctx->Const.UniformBooleanTrue : 0; - } else { - dst[i].i = src[i].i != 0 ? ctx->Const.UniformBooleanTrue : 0; + if (flush) { + /* Find the first element that's different. */ + for (; i < elems; i++) { + if (dst[i] != src[i].u) { + _mesa_flush_vertices_for_uniforms(ctx, uni); + flush = false; + break; + } } + if (flush) + return false; /* No change. */ + } + + /* Set the remaining elements. We know that at least 1 element is + * different and that we have flushed. + */ + for (; i < elems; i++) + dst[i] = src[i].u; + + return true; + } else { + const unsigned elems = components * count; + gl_constant_value *dst = storage; + + if (basicType == GLSL_TYPE_FLOAT) { + unsigned i = 0; + + if (flush) { + /* Find the first element that's different. */ + for (; i < elems; i++) { + if (dst[i].u != + (src[i].f != 0.0f ? ctx->Const.UniformBooleanTrue : 0)) { + _mesa_flush_vertices_for_uniforms(ctx, uni); + flush = false; + break; + } + } + if (flush) + return false; /* No change. */ + } + + /* Set the remaining elements. We know that at least 1 element is + * different and that we have flushed. + */ + for (; i < elems; i++) + dst[i].u = src[i].f != 0.0f ? ctx->Const.UniformBooleanTrue : 0; + + return true; + } else { + unsigned i = 0; + + if (flush) { + /* Find the first element that's different. */ + for (; i < elems; i++) { + if (dst[i].u != + (src[i].u ? ctx->Const.UniformBooleanTrue : 0)) { + _mesa_flush_vertices_for_uniforms(ctx, uni); + flush = false; + break; + } + } + if (flush) + return false; /* No change. */ + } + + /* Set the remaining elements. We know that at least 1 element is + * different and that we have flushed. + */ + for (; i < elems; i++) + dst[i].u = src[i].u ? ctx->Const.UniformBooleanTrue : 0; + + return true; } } } @@ -1151,10 +1217,9 @@ _mesa_uniform(GLint location, GLsizei count, const GLvoid *values, count = MIN2(count, (int) (uni->array_elements - offset)); } - _mesa_flush_vertices_for_uniforms(ctx, uni); - /* Store the data in the "actual type" backing storage for the uniform. */ + bool ctx_flushed = false; gl_constant_value *storage; if (ctx->Const.PackedDriverUniformStorage && (uni->is_bindless || !uni->type->contains_opaque())) { @@ -1162,21 +1227,28 @@ _mesa_uniform(GLint location, GLsizei count, const GLvoid *values, storage = (gl_constant_value *) uni->driver_storage[s].data + (size_mul * offset * components); - copy_uniforms_to_storage(storage, uni, ctx, count, values, size_mul, - offset, components, basicType); + if (copy_uniforms_to_storage(storage, uni, ctx, count, values, size_mul, + offset, components, basicType, !ctx_flushed)) + ctx_flushed = true; } } else { storage = &uni->storage[size_mul * components * offset]; - copy_uniforms_to_storage(storage, uni, ctx, count, values, size_mul, - offset, components, basicType); - - _mesa_propagate_uniforms_to_driver_storage(uni, offset, count); + if (copy_uniforms_to_storage(storage, uni, ctx, count, values, size_mul, + offset, components, basicType, !ctx_flushed)) { + _mesa_propagate_uniforms_to_driver_storage(uni, offset, count); + ctx_flushed = true; + } } + if (!ctx_flushed) + return; /* no change in uniform values */ /* If the uniform is a sampler, do the extra magic necessary to propagate * the changes through. */ if (uni->type->is_sampler()) { + /* Note that samplers are the only uniforms that don't call + * FLUSH_VERTICES above. + */ bool flushed = false; shProg->SamplersValidated = GL_TRUE;