X-Git-Url: https://git.libre-soc.org/?p=mesa.git;a=blobdiff_plain;f=src%2Fmesa%2Fmain%2Funiform_query.cpp;h=db2f173dd2f7ab17b0588dde98cbd8d9570f2ad5;hp=0bee59455a3697bc8dad29fa1d29c8e387201256;hb=bf188f3494b85f550a39dddbf059669c2a9ee324;hpb=005c8e01062e8e88a86904b955d5422742bd32e7 diff --git a/src/mesa/main/uniform_query.cpp b/src/mesa/main/uniform_query.cpp index 0bee59455a3..db2f173dd2f 100644 --- a/src/mesa/main/uniform_query.cpp +++ b/src/mesa/main/uniform_query.cpp @@ -25,18 +25,18 @@ */ #include +#include /* for PRIx64 macro */ +#include -#include "main/core.h" #include "main/context.h" -#include "ir.h" -#include "ir_uniform.h" -#include "program/hash_table.h" -#include "../glsl/program.h" -#include "../glsl/ir_uniform.h" -#include "../glsl/glsl_parser_extras.h" #include "main/shaderapi.h" #include "main/shaderobj.h" -#include "uniforms.h" +#include "main/uniforms.h" +#include "compiler/glsl/ir.h" +#include "compiler/glsl/ir_uniform.h" +#include "compiler/glsl/glsl_parser_extras.h" +#include "compiler/glsl/program.h" +#include "util/bitscan.h" extern "C" void GLAPIENTRY @@ -157,11 +157,11 @@ _mesa_GetActiveUniformsiv(GLuint program, } static struct gl_uniform_storage * -validate_uniform_parameters(struct gl_context *ctx, - struct gl_shader_program *shProg, - GLint location, GLsizei count, - unsigned *array_index, - const char *caller) +validate_uniform_parameters(GLint location, GLsizei count, + unsigned *array_index, + struct gl_context *ctx, + struct gl_shader_program *shProg, + const char *caller) { if (shProg == NULL) { _mesa_error(ctx, GL_INVALID_OPERATION, "%s(program not linked)", caller); @@ -180,10 +180,10 @@ validate_uniform_parameters(struct gl_context *ctx, /* Check that the given location is in bounds of uniform remap table. * Unlinked programs will have NumUniformRemapTable == 0, so we can take - * the shProg->LinkStatus check out of the main path. + * the shProg->data->LinkStatus check out of the main path. */ if (unlikely(location >= (GLint) shProg->NumUniformRemapTable)) { - if (!shProg->LinkStatus) + if (!shProg->data->LinkStatus) _mesa_error(ctx, GL_INVALID_OPERATION, "%s(program not linked)", caller); else @@ -194,7 +194,7 @@ validate_uniform_parameters(struct gl_context *ctx, } if (location == -1) { - if (!shProg->LinkStatus) + if (!shProg->data->LinkStatus) _mesa_error(ctx, GL_INVALID_OPERATION, "%s(program not linked)", caller); @@ -285,8 +285,8 @@ _mesa_get_uniform(struct gl_context *ctx, GLuint program, GLint location, unsigned offset; struct gl_uniform_storage *const uni = - validate_uniform_parameters(ctx, shProg, location, 1, - &offset, "glGetUniform"); + validate_uniform_parameters(location, 1, &offset, + ctx, shProg, "glGetUniform"); if (uni == NULL) { /* For glGetUniform, page 264 (page 278 of the PDF) of the OpenGL 2.1 * spec says: @@ -319,117 +319,341 @@ _mesa_get_uniform(struct gl_context *ctx, GLuint program, GLint location, return; } - if ((uni->type->base_type == GLSL_TYPE_DOUBLE && - returnType != GLSL_TYPE_DOUBLE) || - (uni->type->base_type != GLSL_TYPE_DOUBLE && - returnType == GLSL_TYPE_DOUBLE)) { - _mesa_error( ctx, GL_INVALID_OPERATION, - "glGetnUniform*vARB(incompatible uniform types)"); - return; - } { - unsigned elements = (uni->type->is_sampler()) - ? 1 : uni->type->components(); - const int dmul = uni->type->base_type == GLSL_TYPE_DOUBLE ? 2 : 1; + unsigned elements = uni->type->components(); + const int rmul = glsl_base_type_is_64bit(returnType) ? 2 : 1; + int dmul = (uni->type->is_64bit()) ? 2 : 1; + + if ((uni->type->is_sampler() || uni->type->is_image()) && + !uni->is_bindless) { + /* Non-bindless samplers/images are represented using unsigned integer + * 32-bit, while bindless handles are 64-bit. + */ + dmul = 1; + } /* Calculate the source base address *BEFORE* modifying elements to * account for the size of the user's buffer. */ - const union gl_constant_value *const src = - &uni->storage[offset * elements * dmul]; + const union gl_constant_value *src; + if (ctx->Const.PackedDriverUniformStorage && + (uni->is_bindless || !uni->type->contains_opaque())) { + src = (gl_constant_value *) uni->driver_storage[0].data + + (offset * elements * dmul); + } else { + src = &uni->storage[offset * elements * dmul]; + } assert(returnType == GLSL_TYPE_FLOAT || returnType == GLSL_TYPE_INT || - returnType == GLSL_TYPE_UINT || returnType == GLSL_TYPE_DOUBLE); + returnType == GLSL_TYPE_UINT || returnType == GLSL_TYPE_DOUBLE || + returnType == GLSL_TYPE_UINT64 || returnType == GLSL_TYPE_INT64); /* doubles have a different size than the other 3 types */ - unsigned bytes = sizeof(src[0]) * elements * dmul; + unsigned bytes = sizeof(src[0]) * elements * rmul; if (bufSize < 0 || bytes > (unsigned) bufSize) { - _mesa_error( ctx, GL_INVALID_OPERATION, - "glGetnUniform*vARB(out of bounds: bufSize is %d," - " but %u bytes are required)", bufSize, bytes ); - return; + _mesa_error(ctx, GL_INVALID_OPERATION, + "glGetnUniform*vARB(out of bounds: bufSize is %d," + " but %u bytes are required)", bufSize, bytes); + return; } /* If the return type and the uniform's native type are "compatible," * just memcpy the data. If the types are not compatible, perform a * slower convert-and-copy process. */ - if (returnType == uni->type->base_type - || ((returnType == GLSL_TYPE_INT - || returnType == GLSL_TYPE_UINT) - && - (uni->type->base_type == GLSL_TYPE_INT - || uni->type->base_type == GLSL_TYPE_UINT - || uni->type->base_type == GLSL_TYPE_SAMPLER - || uni->type->base_type == GLSL_TYPE_IMAGE))) { - memcpy(paramsOut, src, bytes); + if (returnType == uni->type->base_type || + ((returnType == GLSL_TYPE_INT || returnType == GLSL_TYPE_UINT) && + (uni->type->is_sampler() || uni->type->is_image())) || + (returnType == GLSL_TYPE_UINT64 && uni->is_bindless)) { + memcpy(paramsOut, src, bytes); } else { - union gl_constant_value *const dst = - (union gl_constant_value *) paramsOut; - - /* This code could be optimized by putting the loop inside the switch - * statements. However, this is not expected to be - * performance-critical code. - */ - for (unsigned i = 0; i < elements; i++) { - switch (returnType) { - case GLSL_TYPE_FLOAT: - switch (uni->type->base_type) { - case GLSL_TYPE_UINT: - dst[i].f = (float) src[i].u; - break; - case GLSL_TYPE_INT: - case GLSL_TYPE_SAMPLER: + union gl_constant_value *const dst = + (union gl_constant_value *) paramsOut; + /* This code could be optimized by putting the loop inside the switch + * statements. However, this is not expected to be + * performance-critical code. + */ + for (unsigned i = 0; i < elements; i++) { + int sidx = i * dmul; + int didx = i * rmul; + + switch (returnType) { + case GLSL_TYPE_FLOAT: + switch (uni->type->base_type) { + case GLSL_TYPE_UINT: + dst[didx].f = (float) src[sidx].u; + break; + case GLSL_TYPE_INT: + case GLSL_TYPE_SAMPLER: case GLSL_TYPE_IMAGE: - dst[i].f = (float) src[i].i; - break; - case GLSL_TYPE_BOOL: - dst[i].f = src[i].i ? 1.0f : 0.0f; - break; - default: - assert(!"Should not get here."); - break; - } - break; - - case GLSL_TYPE_INT: - case GLSL_TYPE_UINT: - switch (uni->type->base_type) { - case GLSL_TYPE_FLOAT: - /* While the GL 3.2 core spec doesn't explicitly - * state how conversion of float uniforms to integer - * values works, in section 6.2 "State Tables" on - * page 267 it says: - * - * "Unless otherwise specified, when floating - * point state is returned as integer values or - * integer state is returned as floating-point - * values it is converted in the fashion - * described in section 6.1.2" - * - * That section, on page 248, says: - * - * "If GetIntegerv or GetInteger64v are called, - * a floating-point value is rounded to the - * nearest integer..." - */ - dst[i].i = IROUND(src[i].f); - break; - case GLSL_TYPE_BOOL: - dst[i].i = src[i].i ? 1 : 0; - break; - default: - assert(!"Should not get here."); - break; - } - break; - - default: - assert(!"Should not get here."); - break; - } - } + dst[didx].f = (float) src[sidx].i; + break; + case GLSL_TYPE_BOOL: + dst[didx].f = src[sidx].i ? 1.0f : 0.0f; + break; + case GLSL_TYPE_DOUBLE: { + double tmp; + memcpy(&tmp, &src[sidx].f, sizeof(tmp)); + dst[didx].f = tmp; + break; + } + case GLSL_TYPE_UINT64: { + uint64_t tmp; + memcpy(&tmp, &src[sidx].u, sizeof(tmp)); + dst[didx].f = tmp; + break; + } + case GLSL_TYPE_INT64: { + uint64_t tmp; + memcpy(&tmp, &src[sidx].i, sizeof(tmp)); + dst[didx].f = tmp; + break; + } + default: + assert(!"Should not get here."); + break; + } + break; + + case GLSL_TYPE_DOUBLE: + switch (uni->type->base_type) { + case GLSL_TYPE_UINT: { + double tmp = src[sidx].u; + memcpy(&dst[didx].f, &tmp, sizeof(tmp)); + break; + } + case GLSL_TYPE_INT: + case GLSL_TYPE_SAMPLER: + case GLSL_TYPE_IMAGE: { + double tmp = src[sidx].i; + memcpy(&dst[didx].f, &tmp, sizeof(tmp)); + break; + } + case GLSL_TYPE_BOOL: { + double tmp = src[sidx].i ? 1.0 : 0.0; + memcpy(&dst[didx].f, &tmp, sizeof(tmp)); + break; + } + case GLSL_TYPE_FLOAT: { + double tmp = src[sidx].f; + memcpy(&dst[didx].f, &tmp, sizeof(tmp)); + break; + } + case GLSL_TYPE_UINT64: { + uint64_t tmpu; + double tmp; + memcpy(&tmpu, &src[sidx].u, sizeof(tmpu)); + tmp = tmpu; + memcpy(&dst[didx].f, &tmp, sizeof(tmp)); + break; + } + case GLSL_TYPE_INT64: { + int64_t tmpi; + double tmp; + memcpy(&tmpi, &src[sidx].i, sizeof(tmpi)); + tmp = tmpi; + memcpy(&dst[didx].f, &tmp, sizeof(tmp)); + break; + } + default: + assert(!"Should not get here."); + break; + } + break; + + case GLSL_TYPE_INT: + switch (uni->type->base_type) { + case GLSL_TYPE_FLOAT: + /* While the GL 3.2 core spec doesn't explicitly + * state how conversion of float uniforms to integer + * values works, in section 6.2 "State Tables" on + * page 267 it says: + * + * "Unless otherwise specified, when floating + * point state is returned as integer values or + * integer state is returned as floating-point + * values it is converted in the fashion + * described in section 6.1.2" + * + * That section, on page 248, says: + * + * "If GetIntegerv or GetInteger64v are called, + * a floating-point value is rounded to the + * nearest integer..." + */ + dst[didx].i = (int64_t) roundf(src[sidx].f); + break; + case GLSL_TYPE_BOOL: + dst[didx].i = src[sidx].i ? 1 : 0; + break; + case GLSL_TYPE_UINT: + dst[didx].i = MIN2(src[sidx].i, INT_MAX); + break; + case GLSL_TYPE_DOUBLE: { + double tmp; + memcpy(&tmp, &src[sidx].f, sizeof(tmp)); + dst[didx].i = (int64_t) round(tmp); + break; + } + case GLSL_TYPE_UINT64: { + uint64_t tmp; + memcpy(&tmp, &src[sidx].u, sizeof(tmp)); + dst[didx].i = tmp; + break; + } + case GLSL_TYPE_INT64: { + int64_t tmp; + memcpy(&tmp, &src[sidx].i, sizeof(tmp)); + dst[didx].i = tmp; + break; + } + default: + assert(!"Should not get here."); + break; + } + break; + + case GLSL_TYPE_UINT: + switch (uni->type->base_type) { + case GLSL_TYPE_FLOAT: + /* The spec isn't terribly clear how to handle negative + * values with an unsigned return type. + * + * GL 4.5 section 2.2.2 ("Data Conversions for State + * Query Commands") says: + * + * "If a value is so large in magnitude that it cannot be + * represented by the returned data type, then the nearest + * value representable using the requested type is + * returned." + */ + dst[didx].u = src[sidx].f < 0.0f ? + 0u : (uint32_t) roundf(src[sidx].f); + break; + case GLSL_TYPE_BOOL: + dst[didx].i = src[sidx].i ? 1 : 0; + break; + case GLSL_TYPE_INT: + dst[didx].i = MAX2(src[sidx].i, 0); + break; + case GLSL_TYPE_DOUBLE: { + double tmp; + memcpy(&tmp, &src[sidx].f, sizeof(tmp)); + dst[didx].u = tmp < 0.0 ? 0u : (uint32_t) round(tmp); + break; + } + case GLSL_TYPE_UINT64: { + uint64_t tmp; + memcpy(&tmp, &src[sidx].u, sizeof(tmp)); + dst[didx].i = MIN2(tmp, INT_MAX); + break; + } + case GLSL_TYPE_INT64: { + int64_t tmp; + memcpy(&tmp, &src[sidx].i, sizeof(tmp)); + dst[didx].i = MAX2(tmp, 0); + break; + } + default: + unreachable("invalid uniform type"); + } + break; + + case GLSL_TYPE_INT64: + switch (uni->type->base_type) { + case GLSL_TYPE_UINT: { + uint64_t tmp = src[sidx].u; + memcpy(&dst[didx].u, &tmp, sizeof(tmp)); + break; + } + case GLSL_TYPE_INT: + case GLSL_TYPE_SAMPLER: + case GLSL_TYPE_IMAGE: { + int64_t tmp = src[sidx].i; + memcpy(&dst[didx].u, &tmp, sizeof(tmp)); + break; + } + case GLSL_TYPE_BOOL: { + int64_t tmp = src[sidx].i ? 1.0f : 0.0f; + memcpy(&dst[didx].u, &tmp, sizeof(tmp)); + break; + } + case GLSL_TYPE_UINT64: { + uint64_t u64; + memcpy(&u64, &src[sidx].u, sizeof(u64)); + int64_t tmp = MIN2(u64, INT_MAX); + memcpy(&dst[didx].u, &tmp, sizeof(tmp)); + break; + } + case GLSL_TYPE_FLOAT: { + int64_t tmp = (int64_t) roundf(src[sidx].f); + memcpy(&dst[didx].u, &tmp, sizeof(tmp)); + break; + } + case GLSL_TYPE_DOUBLE: { + double d; + memcpy(&d, &src[sidx].f, sizeof(d)); + int64_t tmp = (int64_t) round(d); + memcpy(&dst[didx].u, &tmp, sizeof(tmp)); + break; + } + default: + assert(!"Should not get here."); + break; + } + break; + + case GLSL_TYPE_UINT64: + switch (uni->type->base_type) { + case GLSL_TYPE_UINT: { + uint64_t tmp = src[sidx].u; + memcpy(&dst[didx].u, &tmp, sizeof(tmp)); + break; + } + case GLSL_TYPE_INT: + case GLSL_TYPE_SAMPLER: + case GLSL_TYPE_IMAGE: { + int64_t tmp = MAX2(src[sidx].i, 0); + memcpy(&dst[didx].u, &tmp, sizeof(tmp)); + break; + } + case GLSL_TYPE_BOOL: { + int64_t tmp = src[sidx].i ? 1.0f : 0.0f; + memcpy(&dst[didx].u, &tmp, sizeof(tmp)); + break; + } + case GLSL_TYPE_INT64: { + uint64_t i64; + memcpy(&i64, &src[sidx].i, sizeof(i64)); + uint64_t tmp = MAX2(i64, 0); + memcpy(&dst[didx].u, &tmp, sizeof(tmp)); + break; + } + case GLSL_TYPE_FLOAT: { + uint64_t tmp = src[sidx].f < 0.0f ? + 0ull : (uint64_t) roundf(src[sidx].f); + memcpy(&dst[didx].u, &tmp, sizeof(tmp)); + break; + } + case GLSL_TYPE_DOUBLE: { + double d; + memcpy(&d, &src[sidx].f, sizeof(d)); + uint64_t tmp = (d < 0.0) ? 0ull : (uint64_t) round(d); + memcpy(&dst[didx].u, &tmp, sizeof(tmp)); + break; + } + default: + assert(!"Should not get here."); + break; + } + break; + + default: + assert(!"Should not get here."); + break; + } + } } } } @@ -462,12 +686,27 @@ log_uniform(const void *values, enum glsl_base_type basicType, case GLSL_TYPE_INT: printf("%d ", v[i].i); break; + case GLSL_TYPE_UINT64: { + uint64_t tmp; + memcpy(&tmp, &v[i * 2].u, sizeof(tmp)); + printf("%" PRIu64 " ", tmp); + break; + } + case GLSL_TYPE_INT64: { + int64_t tmp; + memcpy(&tmp, &v[i * 2].u, sizeof(tmp)); + printf("%" PRId64 " ", tmp); + break; + } case GLSL_TYPE_FLOAT: printf("%g ", v[i].f); break; - case GLSL_TYPE_DOUBLE: - printf("%g ", *(double* )&v[i * 2].f); + case GLSL_TYPE_DOUBLE: { + double tmp; + memcpy(&tmp, &v[i * 2].f, sizeof(tmp)); + printf("%g ", tmp); break; + } default: assert(!"Should not get here."); break; @@ -490,13 +729,15 @@ log_program_parameters(const struct gl_shader_program *shProg) printf("Program %d %s shader parameters:\n", shProg->Name, _mesa_shader_stage_to_string(i)); for (unsigned j = 0; j < prog->Parameters->NumParameters; j++) { - printf("%s: %p %f %f %f %f\n", + unsigned pvo = prog->Parameters->ParameterValueOffset[j]; + printf("%s: %u %p %f %f %f %f\n", prog->Parameters->Parameters[j].Name, - prog->Parameters->ParameterValues[j], - prog->Parameters->ParameterValues[j][0].f, - prog->Parameters->ParameterValues[j][1].f, - prog->Parameters->ParameterValues[j][2].f, - prog->Parameters->ParameterValues[j][3].f); + pvo, + prog->Parameters->ParameterValues + pvo, + prog->Parameters->ParameterValues[pvo].f, + prog->Parameters->ParameterValues[pvo + 1].f, + prog->Parameters->ParameterValues[pvo + 2].f, + prog->Parameters->ParameterValues[pvo + 3].f); } } fflush(stdout); @@ -524,11 +765,9 @@ _mesa_propagate_uniforms_to_driver_storage(struct gl_uniform_storage *uni, { unsigned i; - /* vector_elements and matrix_columns can be 0 for samplers. - */ - const unsigned components = MAX2(1, uni->type->vector_elements); - const unsigned vectors = MAX2(1, uni->type->matrix_columns); - const int dmul = uni->type->base_type == GLSL_TYPE_DOUBLE ? 2 : 1; + const unsigned components = uni->type->vector_elements; + const unsigned vectors = uni->type->matrix_columns; + const int dmul = uni->type->is_64bit() ? 2 : 1; /* Store the data in the driver's requested type in the driver's storage * areas. @@ -557,14 +796,31 @@ _mesa_propagate_uniforms_to_driver_storage(struct gl_uniform_storage *uni, unsigned j; unsigned v; - for (j = 0; j < count; j++) { - for (v = 0; v < vectors; v++) { - memcpy(dst, src, src_vector_byte_stride); - src += src_vector_byte_stride; - dst += store->vector_stride; + if (src_vector_byte_stride == store->vector_stride) { + if (extra_stride) { + for (j = 0; j < count; j++) { + memcpy(dst, src, src_vector_byte_stride * vectors); + src += src_vector_byte_stride * vectors; + dst += store->vector_stride * vectors; + + dst += extra_stride; + } + } else { + /* Unigine Heaven benchmark gets here */ + memcpy(dst, src, src_vector_byte_stride * vectors * count); + src += src_vector_byte_stride * vectors * count; + dst += store->vector_stride * vectors * count; } + } else { + for (j = 0; j < count; j++) { + for (v = 0; v < vectors; v++) { + memcpy(dst, src, src_vector_byte_stride); + src += src_vector_byte_stride; + dst += store->vector_stride; + } - dst += extra_stride; + dst += extra_stride; + } } break; } @@ -613,6 +869,10 @@ glsl_type_name(enum glsl_base_type type) return "float"; case GLSL_TYPE_DOUBLE: return "double"; + case GLSL_TYPE_UINT64: + return "uint64"; + case GLSL_TYPE_INT64: + return "int64"; case GLSL_TYPE_BOOL: return "bool"; case GLSL_TYPE_SAMPLER: @@ -637,37 +897,28 @@ glsl_type_name(enum glsl_base_type type) } -/** - * Called via glUniform*() functions. - */ -extern "C" void -_mesa_uniform(struct gl_context *ctx, struct gl_shader_program *shProg, - GLint location, GLsizei count, - const GLvoid *values, - enum glsl_base_type basicType, - unsigned src_components) +static struct gl_uniform_storage * +validate_uniform(GLint location, GLsizei count, const GLvoid *values, + unsigned *offset, struct gl_context *ctx, + struct gl_shader_program *shProg, + enum glsl_base_type basicType, unsigned src_components) { - unsigned offset; - int size_mul = basicType == GLSL_TYPE_DOUBLE ? 2 : 1; - struct gl_uniform_storage *const uni = - validate_uniform_parameters(ctx, shProg, location, count, - &offset, "glUniform"); + validate_uniform_parameters(location, count, offset, + ctx, shProg, "glUniform"); if (uni == NULL) - return; + return NULL; if (uni->type->is_matrix()) { /* Can't set matrix uniforms (like mat4) with glUniform */ _mesa_error(ctx, GL_INVALID_OPERATION, "glUniform%u(uniform \"%s\"@%d is matrix)", src_components, uni->name, location); - return; + return NULL; } - /* Verify that the types are compatible. - */ - const unsigned components = uni->type->is_sampler() - ? 1 : uni->type->vector_elements; + /* Verify that the types are compatible. */ + const unsigned components = uni->type->vector_elements; if (components != src_components) { /* glUniformN() must match float/vecN type */ @@ -675,7 +926,7 @@ _mesa_uniform(struct gl_context *ctx, struct gl_shader_program *shProg, "glUniform%u(\"%s\"@%u has %u components, not %u)", src_components, uni->name, location, components, src_components); - return; + return NULL; } bool match; @@ -700,12 +951,12 @@ _mesa_uniform(struct gl_context *ctx, struct gl_shader_program *shProg, src_components, uni->name, location, glsl_type_name(uni->type->base_type), glsl_type_name(basicType)); - return; + return NULL; } if (unlikely(ctx->_Shader->Flags & GLSL_UNIFORMS)) { log_uniform(values, basicType, components, 1, count, - false, shProg, location, uni); + false, shProg, location, uni); } /* Page 100 (page 116 of the PDF) of the OpenGL 3.0 spec says: @@ -727,17 +978,20 @@ _mesa_uniform(struct gl_context *ctx, struct gl_shader_program *shProg, */ if (uni->type->is_sampler()) { for (int i = 0; i < count; i++) { - const unsigned texUnit = ((unsigned *) values)[i]; + const unsigned texUnit = ((unsigned *) values)[i]; /* check that the sampler (tex unit index) is legal */ if (texUnit >= ctx->Const.MaxCombinedTextureImageUnits) { _mesa_error(ctx, GL_INVALID_VALUE, "glUniform1i(invalid sampler/tex unit index for " - "uniform %d)", - location); - return; + "uniform %d)", location); + return NULL; } } + /* We need to reset the validate flag on changes to samplers in case + * two different sampler types are set to the same texture unit. + */ + ctx->_Shader->Validated = GL_FALSE; } if (uni->type->is_image()) { @@ -749,11 +1003,113 @@ _mesa_uniform(struct gl_context *ctx, struct gl_shader_program *shProg, _mesa_error(ctx, GL_INVALID_VALUE, "glUniform1i(invalid image unit index for uniform %d)", location); - return; + return NULL; } } } + return uni; +} + +void +_mesa_flush_vertices_for_uniforms(struct gl_context *ctx, + const struct gl_uniform_storage *uni) +{ + /* Opaque uniforms have no storage unless they are bindless */ + if (!uni->is_bindless && uni->type->contains_opaque()) { + FLUSH_VERTICES(ctx, 0); + return; + } + + uint64_t new_driver_state = 0; + unsigned mask = uni->active_shader_mask; + + while (mask) { + unsigned index = u_bit_scan(&mask); + + assert(index < MESA_SHADER_STAGES); + new_driver_state |= ctx->DriverFlags.NewShaderConstants[index]; + } + + FLUSH_VERTICES(ctx, new_driver_state ? 0 : _NEW_PROGRAM_CONSTANTS); + ctx->NewDriverState |= new_driver_state; +} + +static void +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) +{ + if (!uni->type->is_boolean() && !uni->is_bindless) { + memcpy(storage, values, + sizeof(storage[0]) * components * count * size_mul); + } else if (uni->is_bindless) { + const union gl_constant_value *src = + (const union gl_constant_value *) values; + GLuint64 *dst = (GLuint64 *)&storage->i; + const unsigned elems = components * count; + + 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; + } + } + } +} + + +/** + * Called via glUniform*() functions. + */ +extern "C" void +_mesa_uniform(GLint location, GLsizei count, const GLvoid *values, + struct gl_context *ctx, struct gl_shader_program *shProg, + enum glsl_base_type basicType, unsigned src_components) +{ + unsigned offset; + int size_mul = glsl_base_type_is_64bit(basicType) ? 2 : 1; + + struct gl_uniform_storage *uni; + if (_mesa_is_no_error_enabled(ctx)) { + /* From Seciton 7.6 (UNIFORM VARIABLES) of the OpenGL 4.5 spec: + * + * "If the value of location is -1, the Uniform* commands will + * silently ignore the data passed in, and the current uniform values + * will not be changed. + */ + if (location == -1) + return; + + uni = shProg->UniformRemapTable[location]; + + /* The array index specified by the uniform location is just the + * uniform location minus the base location of of the uniform. + */ + assert(uni->array_elements > 0 || location == (int)uni->remap_location); + offset = location - uni->remap_location; + } else { + uni = validate_uniform(location, count, values, &offset, ctx, shProg, + basicType, src_components); + if (!uni) + return; + } + + const unsigned components = uni->type->vector_elements; + /* Page 82 (page 96 of the PDF) of the OpenGL 2.1 spec says: * * "When loading N elements starting at an arbitrary position k in a @@ -769,80 +1125,84 @@ _mesa_uniform(struct gl_context *ctx, struct gl_shader_program *shProg, count = MIN2(count, (int) (uni->array_elements - offset)); } - FLUSH_VERTICES(ctx, _NEW_PROGRAM_CONSTANTS); + /* We check samplers for changes and flush if needed in the sampler + * handling code further down, so just skip them here. + */ + if (!uni->type->is_sampler()) { + _mesa_flush_vertices_for_uniforms(ctx, uni); + } /* Store the data in the "actual type" backing storage for the uniform. */ - if (!uni->type->is_boolean()) { - memcpy(&uni->storage[size_mul * components * offset], values, - sizeof(uni->storage[0]) * components * count * size_mul); + gl_constant_value *storage; + if (ctx->Const.PackedDriverUniformStorage && + (uni->is_bindless || !uni->type->contains_opaque())) { + for (unsigned s = 0; s < uni->num_driver_storage; s++) { + 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); + } } else { - const union gl_constant_value *src = - (const union gl_constant_value *) values; - union gl_constant_value *dst = &uni->storage[components * offset]; - const unsigned elems = components * count; + storage = &uni->storage[size_mul * components * offset]; + copy_uniforms_to_storage(storage, uni, ctx, count, values, size_mul, + offset, components, basicType); - 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; - } - } + _mesa_propagate_uniforms_to_driver_storage(uni, offset, count); } - uni->initialized = true; - - _mesa_propagate_uniforms_to_driver_storage(uni, offset, count); - /* If the uniform is a sampler, do the extra magic necessary to propagate * the changes through. */ if (uni->type->is_sampler()) { bool flushed = false; + + shProg->SamplersValidated = GL_TRUE; + for (int i = 0; i < MESA_SHADER_STAGES; i++) { - struct gl_shader *const sh = shProg->_LinkedShaders[i]; + struct gl_linked_shader *const sh = shProg->_LinkedShaders[i]; - /* If the shader stage doesn't use the sampler uniform, skip this. - */ - if (sh == NULL || !uni->sampler[i].active) - continue; + /* If the shader stage doesn't use the sampler uniform, skip this. */ + if (!uni->opaque[i].active) + continue; + bool changed = false; for (int j = 0; j < count; j++) { - sh->SamplerUnits[uni->sampler[i].index + offset + j] = - ((unsigned *) values)[j]; + unsigned unit = uni->opaque[i].index + offset + j; + unsigned value = ((unsigned *)values)[j]; + + if (uni->is_bindless) { + struct gl_bindless_sampler *sampler = + &sh->Program->sh.BindlessSamplers[unit]; + + /* Mark this bindless sampler as bound to a texture unit. + */ + if (sampler->unit != value || !sampler->bound) { + sampler->unit = value; + changed = true; + } + sampler->bound = true; + sh->Program->sh.HasBoundBindlessSampler = true; + } else { + if (sh->Program->SamplerUnits[unit] != value) { + sh->Program->SamplerUnits[unit] = value; + changed = true; + } + } } - struct gl_program *const prog = sh->Program; - - assert(sizeof(prog->SamplerUnits) == sizeof(sh->SamplerUnits)); - - /* Determine if any of the samplers used by this shader stage have - * been modified. - */ - bool changed = false; - for (unsigned j = 0; j < ARRAY_SIZE(prog->SamplerUnits); j++) { - if ((sh->active_samplers & (1U << j)) != 0 - && (prog->SamplerUnits[j] != sh->SamplerUnits[j])) { - changed = true; - break; - } - } - - if (changed) { - if (!flushed) { - FLUSH_VERTICES(ctx, _NEW_TEXTURE | _NEW_PROGRAM); - flushed = true; - } - - memcpy(prog->SamplerUnits, - sh->SamplerUnits, - sizeof(sh->SamplerUnits)); + if (changed) { + if (!flushed) { + FLUSH_VERTICES(ctx, _NEW_TEXTURE_OBJECT | _NEW_PROGRAM); + flushed = true; + } - _mesa_update_shader_textures_used(shProg, prog); + struct gl_program *const prog = sh->Program; + _mesa_update_shader_textures_used(shProg, prog); if (ctx->Driver.SamplerUniformChange) - ctx->Driver.SamplerUniformChange(ctx, prog->Target, prog); - } + ctx->Driver.SamplerUniformChange(ctx, prog->Target, prog); + } } } @@ -851,12 +1211,28 @@ _mesa_uniform(struct gl_context *ctx, struct gl_shader_program *shProg, */ if (uni->type->is_image()) { for (int i = 0; i < MESA_SHADER_STAGES; i++) { - if (uni->image[i].active) { - struct gl_shader *sh = shProg->_LinkedShaders[i]; + struct gl_linked_shader *sh = shProg->_LinkedShaders[i]; + + /* If the shader stage doesn't use the image uniform, skip this. */ + if (!uni->opaque[i].active) + continue; - for (int j = 0; j < count; j++) - sh->ImageUnits[uni->image[i].index + offset + j] = - ((GLint *) values)[j]; + for (int j = 0; j < count; j++) { + unsigned unit = uni->opaque[i].index + offset + j; + unsigned value = ((unsigned *)values)[j]; + + if (uni->is_bindless) { + struct gl_bindless_image *image = + &sh->Program->sh.BindlessImages[unit]; + + /* Mark this bindless image as bound to an image unit. + */ + image->unit = value; + image->bound = true; + sh->Program->sh.HasBoundBindlessImage = true; + } else { + sh->Program->sh.ImageUnits[unit] = value; + } } } @@ -864,25 +1240,70 @@ _mesa_uniform(struct gl_context *ctx, struct gl_shader_program *shProg, } } + +static void +copy_uniform_matrix_to_storage(gl_constant_value *storage, + GLsizei count, const void *values, + const unsigned size_mul, const unsigned offset, + const unsigned components, + const unsigned vectors, bool transpose, + unsigned cols, unsigned rows, + enum glsl_base_type basicType) +{ + const unsigned elements = components * vectors; + + if (!transpose) { + memcpy(storage, values, + sizeof(storage[0]) * elements * count * size_mul); + } else if (basicType == GLSL_TYPE_FLOAT) { + /* Copy and transpose the matrix. + */ + const float *src = (const float *)values; + float *dst = &storage->f; + + for (int i = 0; i < count; i++) { + for (unsigned r = 0; r < rows; r++) { + for (unsigned c = 0; c < cols; c++) { + dst[(c * components) + r] = src[c + (r * vectors)]; + } + } + + dst += elements; + src += elements; + } + } else { + assert(basicType == GLSL_TYPE_DOUBLE); + const double *src = (const double *)values; + double *dst = (double *)&storage->f; + + for (int i = 0; i < count; i++) { + for (unsigned r = 0; r < rows; r++) { + for (unsigned c = 0; c < cols; c++) { + dst[(c * components) + r] = src[c + (r * vectors)]; + } + } + + dst += elements; + src += elements; + } + } +} + + /** * Called by glUniformMatrix*() functions. * Note: cols=2, rows=4 ==> array[2] of vec4 */ extern "C" void -_mesa_uniform_matrix(struct gl_context *ctx, struct gl_shader_program *shProg, - GLuint cols, GLuint rows, - GLint location, GLsizei count, - GLboolean transpose, - const GLvoid *values, enum glsl_base_type basicType) +_mesa_uniform_matrix(GLint location, GLsizei count, + GLboolean transpose, const void *values, + struct gl_context *ctx, struct gl_shader_program *shProg, + GLuint cols, GLuint rows, enum glsl_base_type basicType) { unsigned offset; - unsigned vectors; - unsigned components; - unsigned elements; - int size_mul; struct gl_uniform_storage *const uni = - validate_uniform_parameters(ctx, shProg, location, count, - &offset, "glUniformMatrix"); + validate_uniform_parameters(location, count, &offset, + ctx, shProg, "glUniformMatrix"); if (uni == NULL) return; @@ -893,11 +1314,11 @@ _mesa_uniform_matrix(struct gl_context *ctx, struct gl_shader_program *shProg, } assert(basicType == GLSL_TYPE_FLOAT || basicType == GLSL_TYPE_DOUBLE); - size_mul = basicType == GLSL_TYPE_DOUBLE ? 2 : 1; + const unsigned size_mul = basicType == GLSL_TYPE_DOUBLE ? 2 : 1; assert(!uni->type->is_sampler()); - vectors = uni->type->matrix_columns; - components = uni->type->vector_elements; + const unsigned vectors = uni->type->matrix_columns; + const unsigned components = uni->type->vector_elements; /* Verify that the types are compatible. This is greatly simplified for * matrices because they can only have a float base type. @@ -964,64 +1385,216 @@ _mesa_uniform_matrix(struct gl_context *ctx, struct gl_shader_program *shProg, count = MIN2(count, (int) (uni->array_elements - offset)); } - FLUSH_VERTICES(ctx, _NEW_PROGRAM_CONSTANTS); + _mesa_flush_vertices_for_uniforms(ctx, uni); /* Store the data in the "actual type" backing storage for the uniform. */ - elements = components * vectors; + gl_constant_value *storage; + const unsigned elements = components * vectors; + if (ctx->Const.PackedDriverUniformStorage) { + for (unsigned s = 0; s < uni->num_driver_storage; s++) { + storage = (gl_constant_value *) + uni->driver_storage[s].data + (size_mul * offset * elements); + + copy_uniform_matrix_to_storage(storage, count, values, size_mul, + offset, components, vectors, + transpose, cols, rows, basicType); + } + } else { + storage = &uni->storage[size_mul * elements * offset]; + copy_uniform_matrix_to_storage(storage, count, values, size_mul, offset, + components, vectors, transpose, cols, + rows, basicType); - if (!transpose) { - memcpy(&uni->storage[elements * offset], values, - sizeof(uni->storage[0]) * elements * count * size_mul); - } else if (basicType == GLSL_TYPE_FLOAT) { - /* Copy and transpose the matrix. + _mesa_propagate_uniforms_to_driver_storage(uni, offset, count); + } +} + +static void +update_bound_bindless_sampler_flag(struct gl_program *prog) +{ + unsigned i; + + if (likely(!prog->sh.HasBoundBindlessSampler)) + return; + + for (i = 0; i < prog->sh.NumBindlessSamplers; i++) { + struct gl_bindless_sampler *sampler = &prog->sh.BindlessSamplers[i]; + + if (sampler->bound) + return; + } + prog->sh.HasBoundBindlessSampler = false; +} + +static void +update_bound_bindless_image_flag(struct gl_program *prog) +{ + unsigned i; + + if (likely(!prog->sh.HasBoundBindlessImage)) + return; + + for (i = 0; i < prog->sh.NumBindlessImages; i++) { + struct gl_bindless_image *image = &prog->sh.BindlessImages[i]; + + if (image->bound) + return; + } + prog->sh.HasBoundBindlessImage = false; +} + +/** + * Called via glUniformHandleui64*ARB() functions. + */ +extern "C" void +_mesa_uniform_handle(GLint location, GLsizei count, const GLvoid *values, + struct gl_context *ctx, struct gl_shader_program *shProg) +{ + unsigned offset; + struct gl_uniform_storage *uni; + + if (_mesa_is_no_error_enabled(ctx)) { + /* From Section 7.6 (UNIFORM VARIABLES) of the OpenGL 4.5 spec: + * + * "If the value of location is -1, the Uniform* commands will + * silently ignore the data passed in, and the current uniform values + * will not be changed. */ - const float *src = (const float *)values; - float *dst = &uni->storage[elements * offset].f; + if (location == -1) + return; - for (int i = 0; i < count; i++) { - for (unsigned r = 0; r < rows; r++) { - for (unsigned c = 0; c < cols; c++) { - dst[(c * components) + r] = src[c + (r * vectors)]; - } - } + uni = shProg->UniformRemapTable[location]; - dst += elements; - src += elements; + /* The array index specified by the uniform location is just the + * uniform location minus the base location of of the uniform. + */ + assert(uni->array_elements > 0 || location == (int)uni->remap_location); + offset = location - uni->remap_location; + } else { + uni = validate_uniform_parameters(location, count, &offset, + ctx, shProg, "glUniformHandleui64*ARB"); + if (!uni) + return; + + if (!uni->is_bindless) { + /* From section "Errors" of the ARB_bindless_texture spec: + * + * "The error INVALID_OPERATION is generated by + * UniformHandleui64{v}ARB if the sampler or image uniform being + * updated has the "bound_sampler" or "bound_image" layout qualifier." + * + * From section 4.4.6 of the ARB_bindless_texture spec: + * + * "In the absence of these qualifiers, sampler and image uniforms are + * considered "bound". Additionally, if GL_ARB_bindless_texture is + * not enabled, these uniforms are considered "bound"." + */ + _mesa_error(ctx, GL_INVALID_OPERATION, + "glUniformHandleui64*ARB(non-bindless sampler/image uniform)"); + return; + } + } + + const unsigned components = uni->type->vector_elements; + const int size_mul = 2; + + if (unlikely(ctx->_Shader->Flags & GLSL_UNIFORMS)) { + log_uniform(values, GLSL_TYPE_UINT64, components, 1, count, + false, shProg, location, uni); + } + + /* Page 82 (page 96 of the PDF) of the OpenGL 2.1 spec says: + * + * "When loading N elements starting at an arbitrary position k in a + * uniform declared as an array, elements k through k + N - 1 in the + * array will be replaced with the new values. Values for any array + * element that exceeds the highest array element index used, as + * reported by GetActiveUniform, will be ignored by the GL." + * + * Clamp 'count' to a valid value. Note that for non-arrays a count > 1 + * will have already generated an error. + */ + if (uni->array_elements != 0) { + 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. + */ + gl_constant_value *storage; + if (ctx->Const.PackedDriverUniformStorage) { + for (unsigned s = 0; s < uni->num_driver_storage; s++) { + storage = (gl_constant_value *) + uni->driver_storage[s].data + (size_mul * offset * components); + memcpy(storage, values, + sizeof(uni->storage[0]) * components * count * size_mul); } } else { - assert(basicType == GLSL_TYPE_DOUBLE); - const double *src = (const double *)values; - double *dst = (double *)&uni->storage[elements * offset].f; + memcpy(&uni->storage[size_mul * components * offset], values, + sizeof(uni->storage[0]) * components * count * size_mul); - for (int i = 0; i < count; i++) { - for (unsigned r = 0; r < rows; r++) { - for (unsigned c = 0; c < cols; c++) { - dst[(c * components) + r] = src[c + (r * vectors)]; - } - } + _mesa_propagate_uniforms_to_driver_storage(uni, offset, count); + } + + if (uni->type->is_sampler()) { + /* Mark this bindless sampler as not bound to a texture unit because + * it refers to a texture handle. + */ + for (int i = 0; i < MESA_SHADER_STAGES; i++) { + struct gl_linked_shader *const sh = shProg->_LinkedShaders[i]; - dst += elements; - src += elements; + /* If the shader stage doesn't use the sampler uniform, skip this. */ + if (!uni->opaque[i].active) + continue; + + for (int j = 0; j < count; j++) { + unsigned unit = uni->opaque[i].index + offset + j; + struct gl_bindless_sampler *sampler = + &sh->Program->sh.BindlessSamplers[unit]; + + sampler->bound = false; + } + + update_bound_bindless_sampler_flag(sh->Program); } } - uni->initialized = true; + if (uni->type->is_image()) { + /* Mark this bindless image as not bound to an image unit because it + * refers to a texture handle. + */ + for (int i = 0; i < MESA_SHADER_STAGES; i++) { + struct gl_linked_shader *sh = shProg->_LinkedShaders[i]; - _mesa_propagate_uniforms_to_driver_storage(uni, offset, count); -} + /* If the shader stage doesn't use the sampler uniform, skip this. */ + if (!uni->opaque[i].active) + continue; + + for (int j = 0; j < count; j++) { + unsigned unit = uni->opaque[i].index + offset + j; + struct gl_bindless_image *image = + &sh->Program->sh.BindlessImages[unit]; + + image->bound = false; + } + update_bound_bindless_image_flag(sh->Program); + } + } +} extern "C" bool _mesa_sampler_uniforms_are_valid(const struct gl_shader_program *shProg, char *errMsg, size_t errMsgLength) { /* Shader does not have samplers. */ - if (shProg->NumUniformStorage == 0) + if (shProg->data->NumUniformStorage == 0) return true; if (!shProg->SamplersValidated) { - _mesa_snprintf(errMsg, errMsgLength, + snprintf(errMsg, errMsgLength, "active samplers with a different type " "refer to the same texture image unit"); return false; @@ -1046,58 +1619,46 @@ _mesa_sampler_uniforms_pipeline_are_valid(struct gl_pipeline_object *pipeline) * - The number of active samplers in the program exceeds the * maximum number of texture image units allowed." */ + + GLbitfield mask; + GLbitfield TexturesUsed[MAX_COMBINED_TEXTURE_IMAGE_UNITS]; unsigned active_samplers = 0; - const struct gl_shader_program **shProg = - (const struct gl_shader_program **) pipeline->CurrentProgram; + const struct gl_program **prog = + (const struct gl_program **) pipeline->CurrentProgram; + - const glsl_type *unit_types[MAX_COMBINED_TEXTURE_IMAGE_UNITS]; - memset(unit_types, 0, sizeof(unit_types)); + memset(TexturesUsed, 0, sizeof(TexturesUsed)); for (unsigned idx = 0; idx < ARRAY_SIZE(pipeline->CurrentProgram); idx++) { - if (!shProg[idx]) + if (!prog[idx]) continue; - for (unsigned i = 0; i < shProg[idx]->NumUniformStorage; i++) { - const struct gl_uniform_storage *const storage = - &shProg[idx]->UniformStorage[i]; - - if (!storage->type->is_sampler()) + mask = prog[idx]->SamplersUsed; + while (mask) { + const int s = u_bit_scan(&mask); + GLuint unit = prog[idx]->SamplerUnits[s]; + GLuint tgt = prog[idx]->sh.SamplerTargets[s]; + + /* FIXME: Samplers are initialized to 0 and Mesa doesn't do a + * great job of eliminating unused uniforms currently so for now + * don't throw an error if two sampler types both point to 0. + */ + if (unit == 0) continue; - active_samplers++; - - const unsigned count = MAX2(1, storage->array_elements); - for (unsigned j = 0; j < count; j++) { - const unsigned unit = storage->storage[j].i; - - /* FIXME: Samplers are initialized to 0 and Mesa doesn't do a - * great job of eliminating unused uniforms currently so for now - * don't throw an error if two sampler types both point to 0. - */ - if (unit == 0) - continue; - - /* The types of the samplers associated with a particular texture - * unit must be an exact match. Page 74 (page 89 of the PDF) of - * the OpenGL 3.3 core spec says: - * - * "It is not allowed to have variables of different sampler - * types pointing to the same texture image unit within a - * program object." - */ - if (unit_types[unit] == NULL) { - unit_types[unit] = storage->type; - } else if (unit_types[unit] != storage->type) { - pipeline->InfoLog = - ralloc_asprintf(pipeline, - "Texture unit %d is accessed both as %s " - "and %s", - unit, unit_types[unit]->name, - storage->type->name); - return false; - } + if (TexturesUsed[unit] & ~(1 << tgt)) { + pipeline->InfoLog = + ralloc_asprintf(pipeline, + "Program %d: " + "Texture unit %d is accessed with 2 different types", + prog[idx]->Id, unit); + return false; } + + TexturesUsed[unit] |= (1 << tgt); } + + active_samplers += prog[idx]->info.num_textures; } if (active_samplers > MAX_COMBINED_TEXTURE_IMAGE_UNITS) {