mesa: Implement glSpecializeShaderARB

v2:
  * Use gl_spirv_validation instead of spirv_to_nir.  This method just
    validates the shader. The conversion to NIR will happen later,
    during linking. (Alejandro Piñeiro)
  * Use gl_shader_spirv_data struct to store the SPIR-V data.
    (Eduardo Lima)
  * Use the 'spirv_data' member to tell if the gl_shader is a SPIR-V
    shader, instead of a dedicated flag. (Timothy Arceri)

Signed-off-by: Nicolai Hähnle <nicolai.haehnle@amd.com>
Signed-off-by: Alejandro Piñeiro <apinheiro@igalia.com>
Signed-off-by: Eduardo Lima Mitev <elima@igalia.com>

Reviewed-by: Timothy Arceri <tarceri@itsqueeze.com>
This commit is contained in:
Nicolai Hähnle 2017-06-10 21:36:24 +02:00 committed by Alejandro Piñeiro
parent 9063bf7ad8
commit ba975140d3

View file

@ -23,6 +23,11 @@
#include "glspirv.h" #include "glspirv.h"
#include "errors.h" #include "errors.h"
#include "shaderobj.h"
#include "compiler/nir/nir.h"
#include "compiler/spirv/nir_spirv.h"
#include "util/u_atomic.h" #include "util/u_atomic.h"
void void
@ -106,7 +111,105 @@ _mesa_SpecializeShaderARB(GLuint shader,
const GLuint *pConstantValue) const GLuint *pConstantValue)
{ {
GET_CURRENT_CONTEXT(ctx); GET_CURRENT_CONTEXT(ctx);
struct gl_shader *sh;
bool has_entry_point;
struct nir_spirv_specialization *spec_entries = NULL;
/* Just return GL_INVALID_OPERATION error while this is boilerplate */ if (!ctx->Extensions.ARB_gl_spirv) {
_mesa_error(ctx, GL_INVALID_OPERATION, "SpecializeShaderARB"); _mesa_error(ctx, GL_INVALID_OPERATION, "glSpecializeShaderARB");
return;
}
sh = _mesa_lookup_shader_err(ctx, shader, "glSpecializeShaderARB");
if (!sh)
return;
if (!sh->spirv_data) {
_mesa_error(ctx, GL_INVALID_OPERATION,
"glSpecializeShaderARB(not SPIR-V)");
return;
}
if (sh->CompileStatus) {
_mesa_error(ctx, GL_INVALID_OPERATION,
"glSpecializeShaderARB(already specialized)");
return;
}
struct gl_shader_spirv_data *spirv_data = sh->spirv_data;
/* From the GL_ARB_gl_spirv spec:
*
* "The OpenGL API expects the SPIR-V module to have already been
* validated, and can return an error if it discovers anything invalid
* in the module. An invalid SPIR-V module is allowed to result in
* undefined behavior."
*
* However, the following errors still need to be detected (from the same
* spec):
*
* "INVALID_VALUE is generated if <pEntryPoint> does not name a valid
* entry point for <shader>.
*
* INVALID_VALUE is generated if any element of <pConstantIndex>
* refers to a specialization constant that does not exist in the
* shader module contained in <shader>."
*
* We cannot flag those errors a-priori because detecting them requires
* parsing the module. However, flagging them during specialization is okay,
* since it makes no difference in terms of application-visible state.
*/
spec_entries = calloc(sizeof(*spec_entries), numSpecializationConstants);
for (unsigned i = 0; i < numSpecializationConstants; ++i) {
spec_entries[i].id = pConstantIndex[i];
spec_entries[i].data32 = pConstantValue[i];
spec_entries[i].defined_on_module = false;
}
has_entry_point =
gl_spirv_validation((uint32_t *)&spirv_data->SpirVModule->Binary[0],
spirv_data->SpirVModule->Length / 4,
spec_entries, numSpecializationConstants,
sh->Stage, pEntryPoint);
/* See previous spec comment */
if (!has_entry_point) {
_mesa_error(ctx, GL_INVALID_VALUE,
"glSpecializeShaderARB(\"%s\" is not a valid entry point"
" for shader)", pEntryPoint);
goto end;
}
for (unsigned i = 0; i < numSpecializationConstants; ++i) {
if (spec_entries[i].defined_on_module == false) {
_mesa_error(ctx, GL_INVALID_VALUE,
"glSpecializeShaderARB(constant \"%i\" does not exist "
"in shader)", spec_entries[i].id);
goto end;
}
}
spirv_data->SpirVEntryPoint = ralloc_strdup(spirv_data, pEntryPoint);
/* Note that we didn't make a real compilation of the module (spirv_to_nir),
* but just checked some error conditions. Real "compilation" will be done
* later, upon linking.
*/
sh->CompileStatus = COMPILE_SUCCESS;
spirv_data->NumSpecializationConstants = numSpecializationConstants;
spirv_data->SpecializationConstantsIndex =
rzalloc_array_size(spirv_data, sizeof(GLuint),
numSpecializationConstants);
spirv_data->SpecializationConstantsValue =
rzalloc_array_size(spirv_data, sizeof(GLuint),
numSpecializationConstants);
for (unsigned i = 0; i < numSpecializationConstants; ++i) {
spirv_data->SpecializationConstantsIndex[i] = pConstantIndex[i];
spirv_data->SpecializationConstantsValue[i] = pConstantValue[i];
}
end:
free(spec_entries);
} }