mirror of
https://gitlab.freedesktop.org/mesa/mesa.git
synced 2026-05-20 09:08:07 +02:00
They are only set for 1 shader stage. Reviewed-by: Alyssa Rosenzweig <alyssa@rosenzweig.io> Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/36582>
3978 lines
147 KiB
C
3978 lines
147 KiB
C
/*
|
|
* Copyright © 2018 Intel Corporation
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
* copy of this software and associated documentation files (the "Software"),
|
|
* to deal in the Software without restriction, including without limitation
|
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
* and/or sell copies of the Software, and to permit persons to whom the
|
|
* Software is furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice (including the next
|
|
* paragraph) shall be included in all copies or substantial portions of the
|
|
* Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
|
* IN THE SOFTWARE.
|
|
*/
|
|
|
|
#include "nir.h"
|
|
#include "nir_builder.h"
|
|
#include "nir_xfb_info.h"
|
|
#include "gl_nir.h"
|
|
#include "gl_nir_linker.h"
|
|
#include "gl_nir_link_varyings.h"
|
|
#include "linker_util.h"
|
|
#include "string_to_uint_map.h"
|
|
#include "main/shader_types.h"
|
|
#include "main/consts_exts.h"
|
|
#include "main/context.h"
|
|
#include "main/shaderobj.h"
|
|
#include "util/glheader.h"
|
|
#include "util/perf/cpu_trace.h"
|
|
#include "pipe/p_screen.h"
|
|
|
|
/**
|
|
* This file included general link methods, using NIR.
|
|
*/
|
|
|
|
void
|
|
gl_nir_opts(nir_shader *nir)
|
|
{
|
|
bool progress;
|
|
|
|
MESA_TRACE_FUNC();
|
|
|
|
do {
|
|
progress = false;
|
|
|
|
NIR_PASS(_, nir, nir_lower_vars_to_ssa);
|
|
|
|
/* Linking deals with unused inputs/outputs, but here we can remove
|
|
* things local to the shader in the hopes that we can cleanup other
|
|
* things. This pass will also remove variables with only stores, so we
|
|
* might be able to make progress after it.
|
|
*/
|
|
NIR_PASS(progress, nir, nir_remove_dead_variables,
|
|
nir_var_function_temp | nir_var_shader_temp |
|
|
nir_var_mem_shared,
|
|
NULL);
|
|
|
|
NIR_PASS(progress, nir, nir_opt_find_array_copies);
|
|
NIR_PASS(progress, nir, nir_opt_copy_prop_vars);
|
|
NIR_PASS(progress, nir, nir_opt_dead_write_vars);
|
|
|
|
if (nir->options->lower_to_scalar) {
|
|
NIR_PASS(_, nir, nir_lower_alu_to_scalar,
|
|
nir->options->lower_to_scalar_filter, NULL);
|
|
NIR_PASS(_, nir, nir_lower_phis_to_scalar, NULL, NULL);
|
|
}
|
|
|
|
NIR_PASS(_, nir, nir_lower_alu);
|
|
NIR_PASS(_, nir, nir_lower_pack);
|
|
NIR_PASS(progress, nir, nir_copy_prop);
|
|
NIR_PASS(progress, nir, nir_opt_remove_phis);
|
|
NIR_PASS(progress, nir, nir_opt_dce);
|
|
|
|
bool opt_loop_progress = false;
|
|
NIR_PASS(opt_loop_progress, nir, nir_opt_loop);
|
|
if (opt_loop_progress) {
|
|
progress = true;
|
|
NIR_PASS(progress, nir, nir_copy_prop);
|
|
NIR_PASS(progress, nir, nir_opt_dce);
|
|
}
|
|
NIR_PASS(progress, nir, nir_opt_if, 0);
|
|
NIR_PASS(progress, nir, nir_opt_dead_cf);
|
|
NIR_PASS(progress, nir, nir_opt_cse);
|
|
|
|
nir_opt_peephole_select_options peephole_select_options = {
|
|
.limit = 8,
|
|
.indirect_load_ok = true,
|
|
.expensive_alu_ok = true,
|
|
};
|
|
NIR_PASS(progress, nir, nir_opt_peephole_select, &peephole_select_options);
|
|
|
|
NIR_PASS(progress, nir, nir_opt_phi_precision);
|
|
NIR_PASS(progress, nir, nir_opt_algebraic);
|
|
NIR_PASS(progress, nir, nir_opt_constant_folding);
|
|
NIR_PASS(progress, nir, nir_io_add_const_offset_to_base,
|
|
nir_var_shader_in | nir_var_shader_out);
|
|
|
|
if (!nir->info.flrp_lowered) {
|
|
unsigned lower_flrp =
|
|
(nir->options->lower_flrp16 ? 16 : 0) |
|
|
(nir->options->lower_flrp32 ? 32 : 0) |
|
|
(nir->options->lower_flrp64 ? 64 : 0);
|
|
|
|
if (lower_flrp) {
|
|
bool lower_flrp_progress = false;
|
|
|
|
NIR_PASS(lower_flrp_progress, nir, nir_lower_flrp,
|
|
lower_flrp,
|
|
false /* always_precise */);
|
|
if (lower_flrp_progress) {
|
|
NIR_PASS(progress, nir,
|
|
nir_opt_constant_folding);
|
|
progress = true;
|
|
}
|
|
}
|
|
|
|
/* Nothing should rematerialize any flrps, so we only need to do this
|
|
* lowering once.
|
|
*/
|
|
nir->info.flrp_lowered = true;
|
|
}
|
|
|
|
NIR_PASS(progress, nir, nir_opt_undef);
|
|
|
|
peephole_select_options = (nir_opt_peephole_select_options){
|
|
.limit = 0,
|
|
.discard_ok = true,
|
|
};
|
|
NIR_PASS(progress, nir, nir_opt_peephole_select, &peephole_select_options);
|
|
if (nir->options->max_unroll_iterations ||
|
|
(nir->options->max_unroll_iterations_fp64 &&
|
|
(nir->options->lower_doubles_options & nir_lower_fp64_full_software))) {
|
|
NIR_PASS(progress, nir, nir_opt_loop_unroll);
|
|
}
|
|
} while (progress);
|
|
|
|
NIR_PASS(_, nir, nir_lower_var_copies);
|
|
}
|
|
|
|
static void
|
|
replace_tex_src(nir_tex_src *dst, nir_tex_src_type src_type, nir_def *src_def,
|
|
nir_instr *src_parent)
|
|
{
|
|
*dst = nir_tex_src_for_ssa(src_type, src_def);
|
|
nir_src_set_parent_instr(&dst->src, src_parent);
|
|
list_addtail(&dst->src.use_link, &dst->src.ssa->uses);
|
|
}
|
|
|
|
void
|
|
gl_nir_inline_functions(nir_shader *shader)
|
|
{
|
|
/* We have to lower away local constant initializers right before we
|
|
* inline functions. That way they get properly initialized at the top
|
|
* of the function and not at the top of its caller.
|
|
*/
|
|
NIR_PASS(_, shader, nir_lower_variable_initializers, nir_var_all);
|
|
NIR_PASS(_, shader, nir_lower_returns);
|
|
NIR_PASS(_, shader, nir_inline_functions);
|
|
NIR_PASS(_, shader, nir_opt_deref);
|
|
|
|
/* We set func->is_entrypoint after nir_function_create if the function
|
|
* is named "main", so we can use nir_remove_non_entrypoints() for this.
|
|
* Now that we have inlined everything remove all of the functions except
|
|
* func->is_entrypoint.
|
|
*/
|
|
nir_remove_non_entrypoints(shader);
|
|
|
|
/* Now that functions have been inlined remove deref_texture_src intrinisic
|
|
* as we can now see if the texture source is bindless or not.
|
|
*/
|
|
nir_function_impl *impl = nir_shader_get_entrypoint(shader);
|
|
nir_builder b = nir_builder_create(impl);
|
|
|
|
nir_foreach_block(block, impl) {
|
|
nir_foreach_instr_safe(instr, block) {
|
|
if (instr->type == nir_instr_type_tex) {
|
|
nir_tex_instr *intr = nir_instr_as_tex(instr);
|
|
|
|
b.cursor = nir_before_instr(instr);
|
|
|
|
if (intr->src[0].src_type == nir_tex_src_sampler_deref_intrinsic) {
|
|
assert(intr->src[1].src_type == nir_tex_src_texture_deref_intrinsic);
|
|
nir_intrinsic_instr *intrin =
|
|
nir_def_as_intrinsic(intr->src[0].src.ssa);
|
|
nir_deref_instr *deref =
|
|
nir_def_as_deref(intrin->src[0].ssa);
|
|
|
|
/* check for bindless handles */
|
|
if (!nir_deref_mode_is(deref, nir_var_uniform) ||
|
|
nir_deref_instr_get_variable(deref)->data.bindless) {
|
|
nir_def *load = nir_load_deref(&b, deref);
|
|
replace_tex_src(&intr->src[0], nir_tex_src_texture_handle,
|
|
load, instr);
|
|
replace_tex_src(&intr->src[1], nir_tex_src_sampler_handle,
|
|
load, instr);
|
|
} else {
|
|
replace_tex_src(&intr->src[0], nir_tex_src_texture_deref,
|
|
&deref->def, instr);
|
|
replace_tex_src(&intr->src[1], nir_tex_src_sampler_deref,
|
|
&deref->def, instr);
|
|
intr->can_speculate = true;
|
|
}
|
|
nir_instr_remove(&intrin->instr);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
nir_validate_shader(shader, "after function inlining and return lowering");
|
|
}
|
|
|
|
static void
|
|
array_length_to_const(nir_shader *shader)
|
|
{
|
|
nir_foreach_function_impl(impl, shader) {
|
|
nir_builder b = nir_builder_create(impl);
|
|
nir_foreach_block(block, impl) {
|
|
nir_foreach_instr_safe(instr, block) {
|
|
if (instr->type == nir_instr_type_intrinsic) {
|
|
nir_intrinsic_instr *intrin = nir_instr_as_intrinsic(instr);
|
|
if (intrin->intrinsic == nir_intrinsic_deref_implicit_array_length) {
|
|
b.cursor = nir_before_instr(instr);
|
|
nir_deref_instr *deref = nir_src_as_deref(intrin->src[0]);
|
|
assert(deref->deref_type == nir_deref_type_var);
|
|
|
|
unsigned size = glsl_get_length(deref->var->type);
|
|
nir_def *arr_size = nir_imm_intN_t(&b, size, 32);
|
|
nir_def_rewrite_uses(&intrin->def, arr_size);
|
|
nir_instr_remove(&intrin->instr);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
struct emit_vertex_state {
|
|
int max_stream_allowed;
|
|
int invalid_stream_id;
|
|
bool invalid_stream_id_from_emit_vertex;
|
|
bool end_primitive_found;
|
|
unsigned used_streams;
|
|
};
|
|
|
|
/**
|
|
* Determine the highest stream id to which a (geometry) shader emits
|
|
* vertices. Also check whether End{Stream}Primitive is ever called.
|
|
*/
|
|
static void
|
|
find_emit_vertex(struct emit_vertex_state *state, nir_shader *shader) {
|
|
nir_function_impl *impl = nir_shader_get_entrypoint(shader);
|
|
|
|
nir_foreach_block_safe(block, impl) {
|
|
nir_foreach_instr_safe(instr, block) {
|
|
if (instr->type == nir_instr_type_intrinsic) {
|
|
nir_intrinsic_instr *intr = nir_instr_as_intrinsic(instr);
|
|
|
|
if (intr->intrinsic == nir_intrinsic_emit_vertex ||
|
|
intr->intrinsic == nir_intrinsic_end_primitive) {
|
|
int stream_id = nir_intrinsic_stream_id(intr);
|
|
bool from_emit_vertex =
|
|
intr->intrinsic == nir_intrinsic_emit_vertex;
|
|
state->end_primitive_found |=
|
|
intr->intrinsic == nir_intrinsic_end_primitive;
|
|
|
|
if (stream_id < 0) {
|
|
state->invalid_stream_id = stream_id;
|
|
state->invalid_stream_id_from_emit_vertex = from_emit_vertex;
|
|
return;
|
|
}
|
|
|
|
if (stream_id > state->max_stream_allowed) {
|
|
state->invalid_stream_id = stream_id;
|
|
state->invalid_stream_id_from_emit_vertex = from_emit_vertex;
|
|
return;
|
|
}
|
|
|
|
state->used_streams |= 1 << stream_id;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check if geometry shaders emit to non-zero streams and do corresponding
|
|
* validations.
|
|
*/
|
|
static void
|
|
validate_geometry_shader_emissions(const struct gl_constants *consts,
|
|
struct gl_shader_program *prog)
|
|
{
|
|
struct gl_linked_shader *sh = prog->_LinkedShaders[MESA_SHADER_GEOMETRY];
|
|
|
|
if (sh != NULL) {
|
|
struct emit_vertex_state state;
|
|
state.max_stream_allowed = consts->MaxVertexStreams - 1;
|
|
state.invalid_stream_id = 0;
|
|
state.invalid_stream_id_from_emit_vertex = false;
|
|
state.end_primitive_found = false;
|
|
state.used_streams = 0;
|
|
|
|
find_emit_vertex(&state, sh->Program->nir);
|
|
|
|
if (state.invalid_stream_id != 0) {
|
|
linker_error(prog, "Invalid call %s(%d). Accepted values for the "
|
|
"stream parameter are in the range [0, %d].\n",
|
|
state.invalid_stream_id_from_emit_vertex ?
|
|
"EmitStreamVertex" : "EndStreamPrimitive",
|
|
state.invalid_stream_id, state.max_stream_allowed);
|
|
}
|
|
sh->Program->nir->info.gs.active_stream_mask = state.used_streams;
|
|
sh->Program->nir->info.gs.uses_end_primitive = state.end_primitive_found;
|
|
|
|
/* From the ARB_gpu_shader5 spec:
|
|
*
|
|
* "Multiple vertex streams are supported only if the output primitive
|
|
* type is declared to be "points". A program will fail to link if it
|
|
* contains a geometry shader calling EmitStreamVertex() or
|
|
* EndStreamPrimitive() if its output primitive type is not "points".
|
|
*
|
|
* However, in the same spec:
|
|
*
|
|
* "The function EmitVertex() is equivalent to calling EmitStreamVertex()
|
|
* with <stream> set to zero."
|
|
*
|
|
* And:
|
|
*
|
|
* "The function EndPrimitive() is equivalent to calling
|
|
* EndStreamPrimitive() with <stream> set to zero."
|
|
*
|
|
* Since we can call EmitVertex() and EndPrimitive() when we output
|
|
* primitives other than points, calling EmitStreamVertex(0) or
|
|
* EmitEndPrimitive(0) should not produce errors. This it also what Nvidia
|
|
* does. We can use info.gs.active_stream_mask to check whether only the
|
|
* first (zero) stream is active.
|
|
* stream.
|
|
*/
|
|
if (sh->Program->nir->info.gs.active_stream_mask & ~(1 << 0) &&
|
|
sh->Program->nir->info.gs.output_primitive != MESA_PRIM_POINTS) {
|
|
linker_error(prog, "EmitStreamVertex(n) and EndStreamPrimitive(n) "
|
|
"with n>0 requires point output\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
/* For derivatives in compute shaders, GLSL_NV_compute_shader_derivatives
|
|
* states:
|
|
*
|
|
* If neither layout qualifier is specified, derivatives in compute
|
|
* shaders return zero, which is consistent with the handling of built-in
|
|
* texture functions like texture() in GLSL 4.50 compute shaders.
|
|
*/
|
|
static void
|
|
lower_derivatives_without_layout(nir_builder *b)
|
|
{
|
|
if (b->shader->info.stage != MESA_SHADER_COMPUTE ||
|
|
b->shader->info.derivative_group != DERIVATIVE_GROUP_NONE)
|
|
return;
|
|
|
|
nir_foreach_function_impl(impl, b->shader) {
|
|
nir_foreach_block(block, impl) {
|
|
nir_foreach_instr_safe(instr, block) {
|
|
if (instr->type == nir_instr_type_intrinsic) {
|
|
nir_intrinsic_instr *intrin = nir_instr_as_intrinsic(instr);
|
|
nir_intrinsic_op op = intrin->intrinsic;
|
|
if (op != nir_intrinsic_ddx && op != nir_intrinsic_ddx_fine && op != nir_intrinsic_ddx_coarse &&
|
|
op != nir_intrinsic_ddy && op != nir_intrinsic_ddy_fine && op != nir_intrinsic_ddy_coarse)
|
|
continue;
|
|
|
|
nir_def *def = &intrin->def;
|
|
b->cursor = nir_before_instr(instr);
|
|
nir_def *zero = nir_imm_zero(b, def->num_components,
|
|
def->bit_size);
|
|
nir_def_replace(def, zero);
|
|
} else {
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Generate a string describing the mode of a variable
|
|
*/
|
|
const char *
|
|
gl_nir_mode_string(const nir_variable *var)
|
|
{
|
|
switch (var->data.mode) {
|
|
case nir_var_shader_temp:
|
|
return (var->data.read_only) ? "global constant" : "global variable";
|
|
|
|
case nir_var_uniform:
|
|
case nir_var_image:
|
|
case nir_var_mem_ubo:
|
|
return "uniform";
|
|
|
|
case nir_var_mem_ssbo:
|
|
return "buffer";
|
|
|
|
case nir_var_shader_in:
|
|
return "shader input";
|
|
|
|
case nir_var_shader_out:
|
|
return "shader output";
|
|
|
|
case nir_var_system_value:
|
|
return "shader input";
|
|
|
|
case nir_var_function_temp:
|
|
return "local variable";
|
|
|
|
case nir_var_mem_shared:
|
|
return "shader shared";
|
|
|
|
case nir_num_variable_modes:
|
|
break;
|
|
}
|
|
|
|
assert(!"Should not get here.");
|
|
return "invalid variable";
|
|
}
|
|
|
|
static void
|
|
remove_dead_functions(nir_shader *shader)
|
|
{
|
|
struct set *fn_set =
|
|
_mesa_set_create(NULL, _mesa_hash_pointer, _mesa_key_pointer_equal);
|
|
|
|
/* Find all function prototypes adding them to a list then removing them
|
|
* if they are ever called.
|
|
*/
|
|
nir_foreach_function_impl(impl, shader) {
|
|
_mesa_set_add(fn_set, impl->function);
|
|
}
|
|
|
|
nir_foreach_function_impl(impl, shader) {
|
|
nir_foreach_block(block, impl) {
|
|
nir_foreach_instr(instr, block) {
|
|
if (instr->type == nir_instr_type_call) {
|
|
nir_call_instr *call = nir_instr_as_call(instr);
|
|
_mesa_set_remove_key(fn_set, call->callee);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Any functions remaining in the list must be unused so remove them. */
|
|
set_foreach(fn_set, entry) {
|
|
nir_function *func = (nir_function *) entry->key;
|
|
if (!func->is_entrypoint)
|
|
exec_node_remove(&func->node);
|
|
}
|
|
|
|
_mesa_set_destroy(fn_set, NULL);
|
|
}
|
|
|
|
bool
|
|
gl_nir_can_add_pointsize_to_program(const struct gl_constants *consts,
|
|
struct gl_program *prog)
|
|
{
|
|
nir_shader *nir = prog->nir;
|
|
if (!nir)
|
|
return true; /* fixedfunction */
|
|
|
|
assert(nir->info.stage == MESA_SHADER_VERTEX ||
|
|
nir->info.stage == MESA_SHADER_TESS_EVAL ||
|
|
nir->info.stage == MESA_SHADER_GEOMETRY);
|
|
if (nir->info.outputs_written & VARYING_BIT_PSIZ)
|
|
return false;
|
|
|
|
unsigned max_components = nir->info.stage == MESA_SHADER_GEOMETRY ?
|
|
consts->MaxGeometryTotalOutputComponents :
|
|
consts->Program[nir->info.stage].MaxOutputComponents;
|
|
unsigned num_components = 0;
|
|
unsigned needed_components = nir->info.stage == MESA_SHADER_GEOMETRY ? nir->info.gs.vertices_out : 1;
|
|
nir_foreach_shader_out_variable(var, nir) {
|
|
num_components += glsl_count_dword_slots(var->type, false);
|
|
}
|
|
|
|
/* Ensure that there is enough attribute space to emit at least one primitive */
|
|
if (num_components && nir->info.stage == MESA_SHADER_GEOMETRY) {
|
|
if (num_components + needed_components > consts->Program[nir->info.stage].MaxOutputComponents)
|
|
return false;
|
|
num_components *= nir->info.gs.vertices_out;
|
|
}
|
|
|
|
return num_components + needed_components <= max_components;
|
|
}
|
|
|
|
static bool
|
|
can_remove_var(nir_variable *var, UNUSED void *data)
|
|
{
|
|
/* Section 2.11.6 (Uniform Variables) of the OpenGL ES 3.0.3 spec
|
|
* says:
|
|
*
|
|
* "All members of a named uniform block declared with a shared or
|
|
* std140 layout qualifier are considered active, even if they are not
|
|
* referenced in any shader in the program. The uniform block itself is
|
|
* also considered active, even if no member of the block is
|
|
* referenced."
|
|
*
|
|
* Although the spec doesn't state it std430 layouts are expect to behave
|
|
* the same way. If the variable is in a uniform block with one of those
|
|
* layouts, do not eliminate it.
|
|
*/
|
|
if (nir_variable_is_in_block(var) &&
|
|
(glsl_get_ifc_packing(var->interface_type) !=
|
|
GLSL_INTERFACE_PACKING_PACKED))
|
|
return false;
|
|
|
|
if (glsl_get_base_type(glsl_without_array(var->type)) ==
|
|
GLSL_TYPE_SUBROUTINE)
|
|
return false;
|
|
|
|
/* Uniform initializers could get used by another stage. However if its a
|
|
* hidden uniform then it should be safe to remove as this was a constant
|
|
* variable that has been lowered to a uniform.
|
|
*/
|
|
if (var->constant_initializer && var->data.how_declared != nir_var_hidden)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
static void
|
|
set_always_active_io(nir_shader *shader, nir_variable_mode io_mode)
|
|
{
|
|
assert(io_mode == nir_var_shader_in || io_mode == nir_var_shader_out);
|
|
|
|
nir_foreach_variable_with_modes(var, shader, io_mode) {
|
|
/* Don't set always active on builtins that haven't been redeclared */
|
|
if (var->data.how_declared == nir_var_declared_implicitly)
|
|
continue;
|
|
|
|
var->data.always_active_io = true;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* When separate shader programs are enabled, only input/outputs between
|
|
* the stages of a multi-stage separate program can be safely removed
|
|
* from the shader interface. Other inputs/outputs must remain active.
|
|
*/
|
|
static void
|
|
disable_varying_optimizations_for_sso(struct gl_shader_program *prog)
|
|
{
|
|
unsigned first, last;
|
|
assert(prog->SeparateShader);
|
|
|
|
first = MESA_SHADER_STAGES;
|
|
last = 0;
|
|
|
|
/* Determine first and last stage. Excluding the compute stage */
|
|
for (unsigned i = 0; i < MESA_SHADER_COMPUTE; i++) {
|
|
if (!prog->_LinkedShaders[i])
|
|
continue;
|
|
if (first == MESA_SHADER_STAGES)
|
|
first = i;
|
|
last = i;
|
|
}
|
|
|
|
if (first == MESA_SHADER_STAGES)
|
|
return;
|
|
|
|
for (unsigned stage = 0; stage < MESA_SHADER_STAGES; stage++) {
|
|
if (!prog->_LinkedShaders[stage])
|
|
continue;
|
|
|
|
/* Prevent the removal of inputs to the first and outputs from the last
|
|
* stage, unless they are the initial pipeline inputs or final pipeline
|
|
* outputs, respectively.
|
|
*
|
|
* The removal of IO between shaders in the same program is always
|
|
* allowed.
|
|
*/
|
|
if (stage == first && stage != MESA_SHADER_VERTEX) {
|
|
set_always_active_io(prog->_LinkedShaders[stage]->Program->nir,
|
|
nir_var_shader_in);
|
|
}
|
|
|
|
if (stage == last && stage != MESA_SHADER_FRAGMENT) {
|
|
set_always_active_io(prog->_LinkedShaders[stage]->Program->nir,
|
|
nir_var_shader_out);
|
|
}
|
|
}
|
|
}
|
|
|
|
static bool
|
|
inout_has_same_location(const nir_variable *var, unsigned stage)
|
|
{
|
|
if (!var->data.patch &&
|
|
((var->data.mode == nir_var_shader_out &&
|
|
stage == MESA_SHADER_TESS_CTRL) ||
|
|
(var->data.mode == nir_var_shader_in &&
|
|
(stage == MESA_SHADER_TESS_CTRL || stage == MESA_SHADER_TESS_EVAL ||
|
|
stage == MESA_SHADER_GEOMETRY))))
|
|
return true;
|
|
else
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Create gl_shader_variable from nir_variable.
|
|
*/
|
|
static struct gl_shader_variable *
|
|
create_shader_variable(struct gl_shader_program *shProg,
|
|
const nir_variable *in,
|
|
const char *name, const struct glsl_type *type,
|
|
const struct glsl_type *interface_type,
|
|
bool use_implicit_location, int location,
|
|
const struct glsl_type *outermost_struct_type)
|
|
{
|
|
/* Allocate zero-initialized memory to ensure that bitfield padding
|
|
* is zero.
|
|
*/
|
|
struct gl_shader_variable *out = rzalloc(shProg,
|
|
struct gl_shader_variable);
|
|
if (!out)
|
|
return NULL;
|
|
|
|
/* Since gl_VertexID may be lowered to gl_VertexIDMESA, but applications
|
|
* expect to see gl_VertexID in the program resource list. Pretend.
|
|
*/
|
|
if (in->data.mode == nir_var_system_value &&
|
|
in->data.location == SYSTEM_VALUE_VERTEX_ID_ZERO_BASE) {
|
|
out->name.string = ralloc_strdup(shProg, "gl_VertexID");
|
|
} else if ((in->data.mode == nir_var_shader_out &&
|
|
in->data.location == VARYING_SLOT_TESS_LEVEL_OUTER) ||
|
|
(in->data.mode == nir_var_system_value &&
|
|
in->data.location == SYSTEM_VALUE_TESS_LEVEL_OUTER)) {
|
|
out->name.string = ralloc_strdup(shProg, "gl_TessLevelOuter");
|
|
type = glsl_array_type(glsl_float_type(), 4, 0);
|
|
} else if ((in->data.mode == nir_var_shader_out &&
|
|
in->data.location == VARYING_SLOT_TESS_LEVEL_INNER) ||
|
|
(in->data.mode == nir_var_system_value &&
|
|
in->data.location == SYSTEM_VALUE_TESS_LEVEL_INNER)) {
|
|
out->name.string = ralloc_strdup(shProg, "gl_TessLevelInner");
|
|
type = glsl_array_type(glsl_float_type(), 2, 0);
|
|
} else {
|
|
out->name.string = ralloc_strdup(shProg, name);
|
|
}
|
|
|
|
resource_name_updated(&out->name);
|
|
|
|
if (!out->name.string)
|
|
return NULL;
|
|
|
|
/* The ARB_program_interface_query spec says:
|
|
*
|
|
* "Not all active variables are assigned valid locations; the
|
|
* following variables will have an effective location of -1:
|
|
*
|
|
* * uniforms declared as atomic counters;
|
|
*
|
|
* * members of a uniform block;
|
|
*
|
|
* * built-in inputs, outputs, and uniforms (starting with "gl_"); and
|
|
*
|
|
* * inputs or outputs not declared with a "location" layout
|
|
* qualifier, except for vertex shader inputs and fragment shader
|
|
* outputs."
|
|
*/
|
|
if (glsl_get_base_type(in->type) == GLSL_TYPE_ATOMIC_UINT ||
|
|
is_gl_identifier(in->name) ||
|
|
!(in->data.explicit_location || use_implicit_location)) {
|
|
out->location = -1;
|
|
} else {
|
|
out->location = location;
|
|
}
|
|
|
|
out->type = type;
|
|
out->outermost_struct_type = outermost_struct_type;
|
|
out->interface_type = interface_type;
|
|
out->component = in->data.location_frac;
|
|
out->index = in->data.index;
|
|
out->patch = in->data.patch;
|
|
out->mode = in->data.mode;
|
|
out->interpolation = in->data.interpolation;
|
|
out->precision = in->data.precision;
|
|
out->explicit_location = in->data.explicit_location;
|
|
|
|
return out;
|
|
}
|
|
|
|
static bool
|
|
add_shader_variable(const struct gl_constants *consts,
|
|
struct gl_shader_program *shProg,
|
|
struct set *resource_set,
|
|
unsigned stage_mask,
|
|
GLenum programInterface, nir_variable *var,
|
|
const char *name, const struct glsl_type *type,
|
|
bool use_implicit_location, int location,
|
|
bool inouts_share_location,
|
|
const struct glsl_type *outermost_struct_type)
|
|
{
|
|
const struct glsl_type *interface_type = var->interface_type;
|
|
|
|
if (outermost_struct_type == NULL) {
|
|
if (var->data.from_named_ifc_block) {
|
|
const char *interface_name = glsl_get_type_name(interface_type);
|
|
|
|
if (glsl_type_is_array(interface_type)) {
|
|
/* Issue #16 of the ARB_program_interface_query spec says:
|
|
*
|
|
* "* If a variable is a member of an interface block without an
|
|
* instance name, it is enumerated using just the variable name.
|
|
*
|
|
* * If a variable is a member of an interface block with an
|
|
* instance name, it is enumerated as "BlockName.Member", where
|
|
* "BlockName" is the name of the interface block (not the
|
|
* instance name) and "Member" is the name of the variable."
|
|
*
|
|
* In particular, it indicates that it should be "BlockName",
|
|
* not "BlockName[array length]". The conformance suite and
|
|
* dEQP both require this behavior.
|
|
*
|
|
* Here, we unwrap the extra array level added by named interface
|
|
* block array lowering so we have the correct variable type. We
|
|
* also unwrap the interface type when constructing the name.
|
|
*
|
|
* We leave interface_type the same so that ES 3.x SSO pipeline
|
|
* validation can enforce the rules requiring array length to
|
|
* match on interface blocks.
|
|
*/
|
|
type = glsl_get_array_element(type);
|
|
|
|
interface_name =
|
|
glsl_get_type_name(glsl_get_array_element(interface_type));
|
|
}
|
|
|
|
name = ralloc_asprintf(shProg, "%s.%s", interface_name, name);
|
|
}
|
|
}
|
|
|
|
switch (glsl_get_base_type(type)) {
|
|
case GLSL_TYPE_STRUCT: {
|
|
/* The ARB_program_interface_query spec says:
|
|
*
|
|
* "For an active variable declared as a structure, a separate entry
|
|
* will be generated for each active structure member. The name of
|
|
* each entry is formed by concatenating the name of the structure,
|
|
* the "." character, and the name of the structure member. If a
|
|
* structure member to enumerate is itself a structure or array,
|
|
* these enumeration rules are applied recursively."
|
|
*/
|
|
if (outermost_struct_type == NULL)
|
|
outermost_struct_type = type;
|
|
|
|
unsigned field_location = location;
|
|
for (unsigned i = 0; i < glsl_get_length(type); i++) {
|
|
const struct glsl_type *field_type = glsl_get_struct_field(type, i);
|
|
const struct glsl_struct_field *field =
|
|
glsl_get_struct_field_data(type, i);
|
|
|
|
char *field_name = ralloc_asprintf(shProg, "%s.%s", name, field->name);
|
|
if (!add_shader_variable(consts, shProg, resource_set,
|
|
stage_mask, programInterface,
|
|
var, field_name, field_type,
|
|
use_implicit_location, field_location,
|
|
false, outermost_struct_type))
|
|
return false;
|
|
|
|
field_location += glsl_count_attribute_slots(field_type, false);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
case GLSL_TYPE_ARRAY: {
|
|
/* The ARB_program_interface_query spec says:
|
|
*
|
|
* "For an active variable declared as an array of basic types, a
|
|
* single entry will be generated, with its name string formed by
|
|
* concatenating the name of the array and the string "[0]"."
|
|
*
|
|
* "For an active variable declared as an array of an aggregate data
|
|
* type (structures or arrays), a separate entry will be generated
|
|
* for each active array element, unless noted immediately below.
|
|
* The name of each entry is formed by concatenating the name of
|
|
* the array, the "[" character, an integer identifying the element
|
|
* number, and the "]" character. These enumeration rules are
|
|
* applied recursively, treating each enumerated array element as a
|
|
* separate active variable."
|
|
*/
|
|
const struct glsl_type *array_type = glsl_get_array_element(type);
|
|
if (glsl_get_base_type(array_type) == GLSL_TYPE_STRUCT ||
|
|
glsl_get_base_type(array_type) == GLSL_TYPE_ARRAY) {
|
|
unsigned elem_location = location;
|
|
unsigned stride = inouts_share_location ? 0 :
|
|
glsl_count_attribute_slots(array_type, false);
|
|
for (unsigned i = 0; i < glsl_get_length(type); i++) {
|
|
char *elem = ralloc_asprintf(shProg, "%s[%d]", name, i);
|
|
if (!add_shader_variable(consts, shProg, resource_set,
|
|
stage_mask, programInterface,
|
|
var, elem, array_type,
|
|
use_implicit_location, elem_location,
|
|
false, outermost_struct_type))
|
|
return false;
|
|
elem_location += stride;
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
FALLTHROUGH;
|
|
|
|
default: {
|
|
/* The ARB_program_interface_query spec says:
|
|
*
|
|
* "For an active variable declared as a single instance of a basic
|
|
* type, a single entry will be generated, using the variable name
|
|
* from the shader source."
|
|
*/
|
|
struct gl_shader_variable *sha_v =
|
|
create_shader_variable(shProg, var, name, type, interface_type,
|
|
use_implicit_location, location,
|
|
outermost_struct_type);
|
|
if (!sha_v)
|
|
return false;
|
|
|
|
return link_util_add_program_resource(shProg, resource_set,
|
|
programInterface, sha_v, stage_mask);
|
|
}
|
|
}
|
|
}
|
|
|
|
static bool
|
|
add_vars_with_modes(const struct gl_constants *consts,
|
|
struct gl_shader_program *prog, struct set *resource_set,
|
|
nir_shader *nir, nir_variable_mode modes,
|
|
unsigned stage, GLenum programInterface)
|
|
{
|
|
nir_foreach_variable_with_modes(var, nir, modes) {
|
|
if (var->data.how_declared == nir_var_hidden)
|
|
continue;
|
|
|
|
int loc_bias = 0;
|
|
switch(var->data.mode) {
|
|
case nir_var_system_value:
|
|
case nir_var_shader_in:
|
|
if (programInterface != GL_PROGRAM_INPUT)
|
|
continue;
|
|
loc_bias = (stage == MESA_SHADER_VERTEX) ? VERT_ATTRIB_GENERIC0
|
|
: VARYING_SLOT_VAR0;
|
|
break;
|
|
case nir_var_shader_out:
|
|
if (programInterface != GL_PROGRAM_OUTPUT)
|
|
continue;
|
|
loc_bias = (stage == MESA_SHADER_FRAGMENT) ? FRAG_RESULT_DATA0
|
|
: VARYING_SLOT_VAR0;
|
|
break;
|
|
default:
|
|
continue;
|
|
}
|
|
|
|
if (var->data.patch)
|
|
loc_bias = VARYING_SLOT_PATCH0;
|
|
|
|
if (prog->data->spirv) {
|
|
struct gl_shader_variable *sh_var =
|
|
rzalloc(prog, struct gl_shader_variable);
|
|
|
|
/* In the ARB_gl_spirv spec, names are considered optional debug info, so
|
|
* the linker needs to work without them. Returning them is optional.
|
|
* For simplicity, we ignore names.
|
|
*/
|
|
sh_var->name.string = NULL;
|
|
resource_name_updated(&sh_var->name);
|
|
sh_var->type = var->type;
|
|
sh_var->location = var->data.location - loc_bias;
|
|
sh_var->explicit_location = var->data.explicit_location;
|
|
sh_var->index = var->data.index;
|
|
|
|
if (!link_util_add_program_resource(prog, resource_set,
|
|
programInterface,
|
|
sh_var, 1 << stage)) {
|
|
return false;
|
|
}
|
|
} else {
|
|
/* Skip packed varyings, packed varyings are handled separately
|
|
* by add_packed_varyings in the GLSL IR
|
|
* build_program_resource_list() call.
|
|
* TODO: handle packed varyings here instead. We likely want a NIR
|
|
* based packing pass first.
|
|
*/
|
|
if (strncmp(var->name, "packed:", 7) == 0)
|
|
continue;
|
|
|
|
const bool vs_input_or_fs_output =
|
|
(stage == MESA_SHADER_VERTEX &&
|
|
var->data.mode == nir_var_shader_in) ||
|
|
(stage == MESA_SHADER_FRAGMENT &&
|
|
var->data.mode == nir_var_shader_out);
|
|
|
|
if (!add_shader_variable(consts, prog, resource_set,
|
|
1 << stage, programInterface,
|
|
var, var->name, var->type,
|
|
vs_input_or_fs_output,
|
|
var->data.location - loc_bias,
|
|
inout_has_same_location(var, stage),
|
|
NULL))
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool
|
|
add_interface_variables(const struct gl_constants *consts,
|
|
struct gl_shader_program *prog,
|
|
struct set *resource_set,
|
|
unsigned stage, GLenum programInterface)
|
|
{
|
|
struct gl_linked_shader *sh = prog->_LinkedShaders[stage];
|
|
if (!sh)
|
|
return true;
|
|
|
|
nir_shader *nir = sh->Program->nir;
|
|
assert(nir);
|
|
|
|
switch (programInterface) {
|
|
case GL_PROGRAM_INPUT: {
|
|
return add_vars_with_modes(consts, prog, resource_set,
|
|
nir, nir_var_shader_in | nir_var_system_value,
|
|
stage, programInterface);
|
|
}
|
|
case GL_PROGRAM_OUTPUT:
|
|
return add_vars_with_modes(consts, prog, resource_set,
|
|
nir, nir_var_shader_out,
|
|
stage, programInterface);
|
|
default:
|
|
assert("!Should not get here");
|
|
break;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool
|
|
nir_add_packed_var_to_resource_list(const struct gl_constants *consts,
|
|
struct gl_shader_program *shProg,
|
|
struct set *resource_set,
|
|
nir_variable *var,
|
|
unsigned stage, GLenum type)
|
|
{
|
|
if (!add_shader_variable(consts, shProg, resource_set, 1 << stage,
|
|
type, var, var->name, var->type, false,
|
|
var->data.location - VARYING_SLOT_VAR0,
|
|
inout_has_same_location(var, stage), NULL))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Initilise list of program resources that point to resource data.
|
|
*/
|
|
void
|
|
init_program_resource_list(struct gl_shader_program *prog)
|
|
{
|
|
/* Rebuild resource list. */
|
|
if (prog->data->ProgramResourceList) {
|
|
ralloc_free(prog->data->ProgramResourceList);
|
|
prog->data->ProgramResourceList = NULL;
|
|
prog->data->NumProgramResourceList = 0;
|
|
}
|
|
}
|
|
|
|
void
|
|
nir_build_program_resource_list(const struct gl_constants *consts,
|
|
struct gl_shader_program *prog,
|
|
bool rebuild_resourse_list)
|
|
{
|
|
/* Rebuild resource list. */
|
|
if (rebuild_resourse_list)
|
|
init_program_resource_list(prog);
|
|
|
|
int input_stage = MESA_SHADER_STAGES, output_stage = 0;
|
|
|
|
/* Determine first input and final output stage. These are used to
|
|
* detect which variables should be enumerated in the resource list
|
|
* for GL_PROGRAM_INPUT and GL_PROGRAM_OUTPUT.
|
|
*/
|
|
for (unsigned i = 0; i < MESA_SHADER_STAGES; i++) {
|
|
if (!prog->_LinkedShaders[i])
|
|
continue;
|
|
if (input_stage == MESA_SHADER_STAGES)
|
|
input_stage = i;
|
|
output_stage = i;
|
|
}
|
|
|
|
/* Empty shader, no resources. */
|
|
if (input_stage == MESA_SHADER_STAGES && output_stage == 0)
|
|
return;
|
|
|
|
struct set *resource_set = _mesa_pointer_set_create(NULL);
|
|
|
|
/* Add inputs and outputs to the resource list. */
|
|
if (!add_interface_variables(consts, prog, resource_set, input_stage,
|
|
GL_PROGRAM_INPUT)) {
|
|
return;
|
|
}
|
|
|
|
if (!add_interface_variables(consts, prog, resource_set, output_stage,
|
|
GL_PROGRAM_OUTPUT)) {
|
|
return;
|
|
}
|
|
|
|
/* Add transform feedback varyings and buffers. */
|
|
if (prog->last_vert_prog) {
|
|
struct gl_transform_feedback_info *linked_xfb =
|
|
prog->last_vert_prog->sh.LinkedTransformFeedback;
|
|
|
|
/* Add varyings. */
|
|
if (linked_xfb->NumVarying > 0) {
|
|
for (int i = 0; i < linked_xfb->NumVarying; i++) {
|
|
if (!link_util_add_program_resource(prog, resource_set,
|
|
GL_TRANSFORM_FEEDBACK_VARYING,
|
|
&linked_xfb->Varyings[i], 0))
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* Add buffers. */
|
|
for (unsigned i = 0; i < consts->MaxTransformFeedbackBuffers; i++) {
|
|
if ((linked_xfb->ActiveBuffers >> i) & 1) {
|
|
linked_xfb->Buffers[i].Binding = i;
|
|
if (!link_util_add_program_resource(prog, resource_set,
|
|
GL_TRANSFORM_FEEDBACK_BUFFER,
|
|
&linked_xfb->Buffers[i], 0))
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Add uniforms
|
|
*
|
|
* Here, it is expected that nir_link_uniforms() has already been
|
|
* called, so that UniformStorage table is already available.
|
|
*/
|
|
int top_level_array_base_offset = -1;
|
|
int top_level_array_size_in_bytes = -1;
|
|
int second_element_offset = -1;
|
|
int block_index = -1;
|
|
for (unsigned i = 0; i < prog->data->NumUniformStorage; i++) {
|
|
struct gl_uniform_storage *uniform = &prog->data->UniformStorage[i];
|
|
|
|
if (uniform->hidden) {
|
|
for (int j = MESA_SHADER_VERTEX; j < MESA_SHADER_STAGES; j++) {
|
|
if (!uniform->opaque[j].active ||
|
|
glsl_get_base_type(uniform->type) != GLSL_TYPE_SUBROUTINE)
|
|
continue;
|
|
|
|
GLenum type =
|
|
_mesa_shader_stage_to_subroutine_uniform((mesa_shader_stage)j);
|
|
/* add shader subroutines */
|
|
if (!link_util_add_program_resource(prog, resource_set,
|
|
type, uniform, 0))
|
|
return;
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
if (!link_util_should_add_buffer_variable(prog, uniform,
|
|
top_level_array_base_offset,
|
|
top_level_array_size_in_bytes,
|
|
second_element_offset, block_index))
|
|
continue;
|
|
|
|
|
|
if (prog->data->UniformStorage[i].offset >= second_element_offset) {
|
|
top_level_array_base_offset =
|
|
prog->data->UniformStorage[i].offset;
|
|
|
|
top_level_array_size_in_bytes =
|
|
prog->data->UniformStorage[i].top_level_array_size *
|
|
prog->data->UniformStorage[i].top_level_array_stride;
|
|
|
|
/* Set or reset the second element offset. For non arrays this
|
|
* will be set to -1.
|
|
*/
|
|
second_element_offset = top_level_array_size_in_bytes ?
|
|
top_level_array_base_offset +
|
|
prog->data->UniformStorage[i].top_level_array_stride : -1;
|
|
}
|
|
block_index = uniform->block_index;
|
|
|
|
|
|
GLenum interface = uniform->is_shader_storage ? GL_BUFFER_VARIABLE : GL_UNIFORM;
|
|
if (!link_util_add_program_resource(prog, resource_set, interface, uniform,
|
|
uniform->active_shader_mask)) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
for (unsigned i = 0; i < prog->data->NumUniformBlocks; i++) {
|
|
if (!link_util_add_program_resource(prog, resource_set, GL_UNIFORM_BLOCK,
|
|
&prog->data->UniformBlocks[i],
|
|
prog->data->UniformBlocks[i].stageref))
|
|
return;
|
|
}
|
|
|
|
for (unsigned i = 0; i < prog->data->NumShaderStorageBlocks; i++) {
|
|
if (!link_util_add_program_resource(prog, resource_set, GL_SHADER_STORAGE_BLOCK,
|
|
&prog->data->ShaderStorageBlocks[i],
|
|
prog->data->ShaderStorageBlocks[i].stageref))
|
|
return;
|
|
}
|
|
|
|
/* Add atomic counter buffers. */
|
|
for (unsigned i = 0; i < prog->data->NumAtomicBuffers; i++) {
|
|
if (!link_util_add_program_resource(prog, resource_set, GL_ATOMIC_COUNTER_BUFFER,
|
|
&prog->data->AtomicBuffers[i], 0))
|
|
return;
|
|
}
|
|
|
|
unsigned mask = prog->data->linked_stages;
|
|
while (mask) {
|
|
const int i = u_bit_scan(&mask);
|
|
struct gl_program *p = prog->_LinkedShaders[i]->Program;
|
|
|
|
GLuint type = _mesa_shader_stage_to_subroutine((mesa_shader_stage)i);
|
|
for (unsigned j = 0; j < p->sh.NumSubroutineFunctions; j++) {
|
|
if (!link_util_add_program_resource(prog, resource_set,
|
|
type,
|
|
&p->sh.SubroutineFunctions[j],
|
|
0))
|
|
return;
|
|
}
|
|
}
|
|
|
|
_mesa_set_destroy(resource_set, NULL);
|
|
}
|
|
|
|
static void
|
|
shared_type_info(const struct glsl_type *type, unsigned *size, unsigned *align)
|
|
{
|
|
assert(glsl_type_is_vector_or_scalar(type));
|
|
|
|
uint32_t comp_size = glsl_type_is_boolean(type)
|
|
? 4 : glsl_get_bit_size(type) / 8;
|
|
unsigned length = glsl_get_vector_elements(type);
|
|
*size = comp_size * length,
|
|
*align = comp_size * (length == 3 ? 4 : length);
|
|
}
|
|
|
|
static bool
|
|
can_remove_varying_before_linking(nir_variable *var, void *data)
|
|
{
|
|
bool *is_sso = (bool *) data;
|
|
if (*is_sso) {
|
|
/* Allow the removal of unused builtins in SSO */
|
|
return var->data.location > -1 && var->data.location < VARYING_SLOT_VAR0;
|
|
} else
|
|
return true;
|
|
}
|
|
|
|
static void
|
|
remove_dead_varyings_pre_linking(nir_shader *nir)
|
|
{
|
|
struct nir_remove_dead_variables_options opts;
|
|
bool is_sso = nir->info.separate_shader;
|
|
opts.can_remove_var_data = &is_sso;
|
|
opts.can_remove_var = &can_remove_varying_before_linking;
|
|
nir_variable_mode mask = nir_var_shader_in | nir_var_shader_out;
|
|
nir_remove_dead_variables(nir, mask, &opts);
|
|
}
|
|
|
|
/* - create a gl_PointSize variable
|
|
* - find every gl_Position write
|
|
* - store 1.0 to gl_PointSize after every gl_Position write
|
|
*/
|
|
bool
|
|
gl_nir_add_point_size(nir_shader *nir)
|
|
{
|
|
nir_variable *psiz = nir_create_variable_with_location(nir, nir_var_shader_out,
|
|
VARYING_SLOT_PSIZ, glsl_float_type());
|
|
psiz->data.how_declared = nir_var_hidden;
|
|
|
|
nir_function_impl *impl = nir_shader_get_entrypoint(nir);
|
|
nir_builder b = nir_builder_create(impl);
|
|
bool found = false;
|
|
nir_foreach_block_safe(block, impl) {
|
|
nir_foreach_instr_safe(instr, block) {
|
|
if (instr->type == nir_instr_type_intrinsic) {
|
|
nir_intrinsic_instr *intr = nir_instr_as_intrinsic(instr);
|
|
if (intr->intrinsic == nir_intrinsic_store_deref ||
|
|
intr->intrinsic == nir_intrinsic_copy_deref) {
|
|
nir_variable *var = nir_intrinsic_get_var(intr, 0);
|
|
if (var->data.location == VARYING_SLOT_POS) {
|
|
b.cursor = nir_after_instr(instr);
|
|
nir_deref_instr *deref = nir_build_deref_var(&b, psiz);
|
|
nir_store_deref(&b, deref, nir_imm_float(&b, 1.0), BITFIELD_BIT(0));
|
|
found = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (!found) {
|
|
b.cursor = nir_before_impl(impl);
|
|
nir_deref_instr *deref = nir_build_deref_var(&b, psiz);
|
|
nir_store_deref(&b, deref, nir_imm_float(&b, 1.0), BITFIELD_BIT(0));
|
|
}
|
|
|
|
nir->info.outputs_written |= VARYING_BIT_PSIZ;
|
|
|
|
/* We always modify the entrypoint */
|
|
return nir_progress(true, impl, nir_metadata_control_flow);
|
|
}
|
|
|
|
static void
|
|
zero_array_members(nir_builder *b, nir_variable *var)
|
|
{
|
|
nir_deref_instr *deref = nir_build_deref_var(b, var);
|
|
nir_def *zero = nir_imm_zero(b, 4, 32);
|
|
for (int i = 0; i < glsl_array_size(var->type); i++) {
|
|
nir_deref_instr *arr = nir_build_deref_array_imm(b, deref, i);
|
|
uint32_t mask = BITFIELD_MASK(glsl_get_vector_elements(arr->type));
|
|
nir_store_deref(b, arr, nir_channels(b, zero, mask), mask);
|
|
}
|
|
}
|
|
|
|
/* GL has an implicit default of 0 for unwritten gl_ClipDistance members;
|
|
* to achieve this, write 0 to all members at the start of the shader and
|
|
* let them be naturally overwritten later
|
|
*/
|
|
static bool
|
|
gl_nir_zero_initialize_clip_distance(nir_shader *nir)
|
|
{
|
|
nir_variable *clip_dist0 = nir_find_variable_with_location(nir, nir_var_shader_out, VARYING_SLOT_CLIP_DIST0);
|
|
nir_variable *clip_dist1 = nir_find_variable_with_location(nir, nir_var_shader_out, VARYING_SLOT_CLIP_DIST1);
|
|
if (!clip_dist0 && !clip_dist1)
|
|
return false;
|
|
|
|
nir_function_impl *impl = nir_shader_get_entrypoint(nir);
|
|
nir_builder b = nir_builder_at(nir_before_impl(impl));
|
|
if (clip_dist0)
|
|
zero_array_members(&b, clip_dist0);
|
|
|
|
if (clip_dist1)
|
|
zero_array_members(&b, clip_dist1);
|
|
|
|
return nir_progress(true, impl, nir_metadata_control_flow);
|
|
}
|
|
|
|
static void
|
|
lower_patch_vertices_in(struct gl_shader_program *shader_prog)
|
|
{
|
|
struct gl_linked_shader *linked_tcs =
|
|
shader_prog->_LinkedShaders[MESA_SHADER_TESS_CTRL];
|
|
struct gl_linked_shader *linked_tes =
|
|
shader_prog->_LinkedShaders[MESA_SHADER_TESS_EVAL];
|
|
|
|
/* If we have a TCS and TES linked together, lower TES patch vertices. */
|
|
if (linked_tcs && linked_tes) {
|
|
nir_shader *tcs_nir = linked_tcs->Program->nir;
|
|
nir_shader *tes_nir = linked_tes->Program->nir;
|
|
|
|
/* The TES input vertex count is the TCS output vertex count,
|
|
* lower TES gl_PatchVerticesIn to a constant.
|
|
*/
|
|
uint32_t tes_patch_verts = tcs_nir->info.tess.tcs_vertices_out;
|
|
NIR_PASS(_, tes_nir, nir_lower_patch_vertices, tes_patch_verts, NULL);
|
|
}
|
|
}
|
|
|
|
static void
|
|
preprocess_shader(const struct pipe_screen *screen,
|
|
const struct gl_constants *consts,
|
|
const struct gl_extensions *exts,
|
|
struct gl_program *prog,
|
|
struct gl_shader_program *shader_program,
|
|
mesa_shader_stage stage)
|
|
{
|
|
const nir_shader_compiler_options *options = screen->nir_options[prog->info.stage];
|
|
assert(options);
|
|
|
|
nir_shader *nir = prog->nir;
|
|
nir_shader_gather_info(prog->nir, nir_shader_get_entrypoint(prog->nir));
|
|
|
|
if (prog->info.stage == MESA_SHADER_FRAGMENT && consts->HasFBFetch) {
|
|
NIR_PASS(_, prog->nir, gl_nir_lower_blend_equation_advanced,
|
|
exts->KHR_blend_equation_advanced_coherent);
|
|
}
|
|
|
|
/* Set the next shader stage hint for VS and TES. */
|
|
if (!nir->info.separate_shader) {
|
|
unsigned prev_stages = shader_program->data->linked_stages &
|
|
BITFIELD_MASK(prog->info.stage);
|
|
unsigned next_stages = shader_program->data->linked_stages &
|
|
~BITFIELD_MASK(prog->info.stage + 1);
|
|
|
|
if (prev_stages) {
|
|
nir->info.prev_stage = util_last_bit(prev_stages) - 1;
|
|
|
|
if (nir->info.stage == MESA_SHADER_FRAGMENT) {
|
|
nir->info.prev_stage_has_xfb =
|
|
shader_program->TransformFeedback.NumVarying > 0;
|
|
}
|
|
}
|
|
|
|
if (next_stages)
|
|
nir->info.next_stage = u_bit_scan(&next_stages);
|
|
}
|
|
|
|
prog->skip_pointsize_xfb = !(nir->info.outputs_written & VARYING_BIT_PSIZ);
|
|
if (!consts->PointSizeFixed && prog->skip_pointsize_xfb &&
|
|
stage < MESA_SHADER_FRAGMENT && stage != MESA_SHADER_TESS_CTRL &&
|
|
gl_nir_can_add_pointsize_to_program(consts, prog)) {
|
|
NIR_PASS(_, nir, gl_nir_add_point_size);
|
|
}
|
|
|
|
if (stage < MESA_SHADER_FRAGMENT && stage != MESA_SHADER_TESS_CTRL &&
|
|
(nir->info.outputs_written & (VARYING_BIT_CLIP_DIST0 | VARYING_BIT_CLIP_DIST1)))
|
|
NIR_PASS(_, nir, gl_nir_zero_initialize_clip_distance);
|
|
|
|
NIR_PASS(_, nir, nir_lower_global_vars_to_local);
|
|
NIR_PASS(_, nir, nir_lower_var_copies);
|
|
|
|
if (screen->shader_caps[nir->info.stage].fp16 &&
|
|
screen->shader_caps[nir->info.stage].int16) {
|
|
NIR_PASS(_, nir, nir_lower_mediump_vars, nir_var_function_temp | nir_var_shader_temp | nir_var_mem_shared);
|
|
}
|
|
|
|
if (options->lower_to_scalar) {
|
|
NIR_PASS(_, nir, nir_remove_dead_variables,
|
|
nir_var_function_temp | nir_var_shader_temp |
|
|
nir_var_mem_shared, NULL);
|
|
NIR_PASS(_, nir, nir_opt_copy_prop_vars);
|
|
NIR_PASS(_, nir, nir_lower_alu_to_scalar,
|
|
options->lower_to_scalar_filter, NULL);
|
|
}
|
|
|
|
NIR_PASS(_, nir, nir_opt_barrier_modes);
|
|
|
|
/* before buffers and vars_to_ssa */
|
|
NIR_PASS(_, nir, gl_nir_lower_images, true);
|
|
|
|
if (prog->nir->info.stage == MESA_SHADER_COMPUTE) {
|
|
NIR_PASS(_, prog->nir, nir_lower_vars_to_explicit_types,
|
|
nir_var_mem_shared, shared_type_info);
|
|
NIR_PASS(_, prog->nir, nir_lower_explicit_io,
|
|
nir_var_mem_shared, nir_address_format_32bit_offset);
|
|
}
|
|
|
|
/* Do a round of constant folding to clean up address calculations */
|
|
NIR_PASS(_, nir, nir_opt_constant_folding);
|
|
}
|
|
|
|
static bool
|
|
prelink_lowering(const struct pipe_screen *screen,
|
|
const struct gl_constants *consts,
|
|
const struct gl_extensions *exts,
|
|
struct gl_shader_program *shader_program,
|
|
struct gl_linked_shader **linked_shader, unsigned num_shaders)
|
|
{
|
|
for (unsigned i = 0; i < num_shaders; i++) {
|
|
struct gl_linked_shader *shader = linked_shader[i];
|
|
const nir_shader_compiler_options *options = screen->nir_options[shader->Stage];
|
|
struct gl_program *prog = shader->Program;
|
|
|
|
/* NIR drivers that support tess shaders and compact arrays need to use
|
|
* GLSLTessLevelsAsInputs / pipe_caps.glsl_tess_levels_as_inputs. The NIR
|
|
* linker doesn't support linking these as compat arrays of sysvals.
|
|
*/
|
|
assert(consts->GLSLTessLevelsAsInputs || !options->compact_arrays ||
|
|
!exts->ARB_tessellation_shader);
|
|
|
|
|
|
/* ES 3.0+ vertex shaders may still have dead varyings but its now safe
|
|
* to remove them as validation is now done according to the spec.
|
|
*/
|
|
if (shader_program->IsES && shader_program->GLSL_Version >= 300 &&
|
|
i == MESA_SHADER_VERTEX)
|
|
remove_dead_varyings_pre_linking(prog->nir);
|
|
|
|
preprocess_shader(screen, consts, exts, prog, shader_program, shader->Stage);
|
|
|
|
if (prog->nir->info.shared_size > consts->MaxComputeSharedMemorySize) {
|
|
linker_error(shader_program, "Too much shared memory used (%u/%u)\n",
|
|
prog->nir->info.shared_size,
|
|
consts->MaxComputeSharedMemorySize);
|
|
return false;
|
|
}
|
|
|
|
if (options->lower_to_scalar) {
|
|
NIR_PASS(_, shader->Program->nir, nir_lower_load_const_to_scalar);
|
|
}
|
|
}
|
|
|
|
lower_patch_vertices_in(shader_program);
|
|
|
|
/* Linking shaders also optimizes them. Separate shaders, compute shaders
|
|
* and shaders with a fixed-func VS or FS that don't need linking are
|
|
* optimized here.
|
|
*/
|
|
if (num_shaders == 1)
|
|
gl_nir_opts(linked_shader[0]->Program->nir);
|
|
|
|
/* nir_opt_access() needs to run before linking so that ImageAccess[]
|
|
* and BindlessImage[].access are filled out with the correct modes.
|
|
*/
|
|
for (unsigned i = 0; i < num_shaders; i++) {
|
|
nir_shader *nir = linked_shader[i]->Program->nir;
|
|
|
|
nir_opt_access_options opt_access_options;
|
|
opt_access_options.is_vulkan = false;
|
|
NIR_PASS(_, nir, nir_opt_access, &opt_access_options);
|
|
|
|
if (!nir->options->compact_arrays) {
|
|
NIR_PASS(_, nir, nir_lower_clip_cull_distance_to_vec4s);
|
|
NIR_PASS(_, nir, nir_lower_tess_level_array_vars_to_vec);
|
|
}
|
|
|
|
/* Combine clip and cull outputs into one array and set:
|
|
* - shader_info::clip_distance_array_size
|
|
* - shader_info::cull_distance_array_size
|
|
*/
|
|
if (!(nir->options->io_options &
|
|
nir_io_separate_clip_cull_distance_arrays))
|
|
NIR_PASS(_, nir, nir_lower_clip_cull_distance_array_vars);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Lower load_deref and store_deref on input/output variables to load_input
|
|
* and store_output intrinsics, and perform varying optimizations and
|
|
* compaction.
|
|
*/
|
|
void
|
|
gl_nir_lower_optimize_varyings(const struct gl_constants *consts,
|
|
struct gl_shader_program *prog, bool spirv)
|
|
{
|
|
nir_shader *shaders[MESA_SHADER_STAGES];
|
|
unsigned num_shaders = 0;
|
|
unsigned max_ubos = UINT_MAX;
|
|
unsigned max_uniform_comps = UINT_MAX;
|
|
|
|
for (unsigned i = 0; i < MESA_SHADER_STAGES; i++) {
|
|
struct gl_linked_shader *shader = prog->_LinkedShaders[i];
|
|
|
|
if (!shader)
|
|
continue;
|
|
|
|
nir_shader *nir = shader->Program->nir;
|
|
|
|
if (nir->info.stage == MESA_SHADER_COMPUTE)
|
|
return;
|
|
|
|
shaders[num_shaders] = nir;
|
|
max_uniform_comps = MIN2(max_uniform_comps,
|
|
consts->Program[i].MaxUniformComponents);
|
|
max_ubos = MIN2(max_ubos, consts->Program[i].MaxUniformBlocks);
|
|
num_shaders++;
|
|
}
|
|
|
|
/* Lower IO derefs to load and store intrinsics. */
|
|
for (unsigned i = 0; i < num_shaders; i++)
|
|
nir_lower_io_passes(shaders[i], true);
|
|
|
|
if (debug_get_bool_option("MESA_GLSL_DISABLE_IO_OPT", false))
|
|
return;
|
|
|
|
nir_opt_varyings_bulk(shaders, num_shaders, spirv, max_uniform_comps,
|
|
max_ubos, gl_nir_opts);
|
|
}
|
|
|
|
bool
|
|
gl_nir_link_spirv(const struct pipe_screen *screen,
|
|
const struct gl_constants *consts,
|
|
const struct gl_extensions *exts,
|
|
struct gl_shader_program *prog,
|
|
const struct gl_nir_linker_options *options)
|
|
{
|
|
struct gl_linked_shader *linked_shader[MESA_SHADER_STAGES];
|
|
unsigned num_shaders = 0;
|
|
|
|
MESA_TRACE_FUNC();
|
|
|
|
for (unsigned i = 0; i < MESA_SHADER_STAGES; i++) {
|
|
if (prog->_LinkedShaders[i]) {
|
|
linked_shader[num_shaders++] = prog->_LinkedShaders[i];
|
|
|
|
remove_dead_varyings_pre_linking(prog->_LinkedShaders[i]->Program->nir);
|
|
}
|
|
}
|
|
|
|
gl_nir_link_assign_xfb_resources(consts, prog);
|
|
|
|
if (!prelink_lowering(screen, consts, exts, prog, linked_shader, num_shaders))
|
|
return false;
|
|
|
|
gl_nir_lower_optimize_varyings(consts, prog, true);
|
|
|
|
for (unsigned i = 0; i < MESA_SHADER_STAGES; i++) {
|
|
struct gl_linked_shader *shader = prog->_LinkedShaders[i];
|
|
if (shader) {
|
|
const nir_remove_dead_variables_options opts = {
|
|
.can_remove_var = can_remove_var,
|
|
};
|
|
nir_remove_dead_variables(shader->Program->nir,
|
|
nir_var_uniform | nir_var_image,
|
|
&opts);
|
|
}
|
|
}
|
|
|
|
if (!gl_nir_link_uniform_blocks(consts, prog))
|
|
return false;
|
|
|
|
if (!gl_nir_link_uniforms(consts, prog, options->fill_parameters))
|
|
return false;
|
|
|
|
gl_nir_link_assign_atomic_counter_resources(consts, prog);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
gl_nir_validate_intrastage_arrays(struct gl_shader_program *prog,
|
|
nir_variable *var, nir_variable *existing,
|
|
nir_shader *existing_shader,
|
|
bool match_precision)
|
|
{
|
|
/* Consider the types to be "the same" if both types are arrays
|
|
* of the same type and one of the arrays is implicitly sized.
|
|
* In addition, set the type of the linked variable to the
|
|
* explicitly sized array.
|
|
*/
|
|
if (glsl_type_is_array(var->type) && glsl_type_is_array(existing->type)) {
|
|
const glsl_type *no_array_var = glsl_get_array_element(var->type);
|
|
const glsl_type *no_array_existing =
|
|
glsl_get_array_element(existing->type);
|
|
bool type_matches;
|
|
|
|
type_matches = (match_precision ?
|
|
no_array_var == no_array_existing :
|
|
glsl_type_compare_no_precision(no_array_var, no_array_existing));
|
|
|
|
if (type_matches &&
|
|
((glsl_array_size(var->type) == 0) ||
|
|
(glsl_array_size(existing->type) == 0))) {
|
|
if (glsl_array_size(var->type) != 0) {
|
|
if ((int)glsl_array_size(var->type) <=
|
|
existing->data.max_array_access) {
|
|
linker_error(prog, "%s `%s' declared as type "
|
|
"`%s' but outermost dimension has an index"
|
|
" of `%i'\n",
|
|
gl_nir_mode_string(var),
|
|
var->name, glsl_get_type_name(var->type),
|
|
existing->data.max_array_access);
|
|
}
|
|
existing->type = var->type;
|
|
nir_fixup_deref_types(existing_shader);
|
|
return true;
|
|
} else if (glsl_array_size(existing->type) != 0) {
|
|
if((int)glsl_array_size(existing->type) <= var->data.max_array_access &&
|
|
!existing->data.from_ssbo_unsized_array) {
|
|
linker_error(prog, "%s `%s' declared as type "
|
|
"`%s' but outermost dimension has an index"
|
|
" of `%i'\n",
|
|
gl_nir_mode_string(var),
|
|
var->name, glsl_get_type_name(existing->type),
|
|
var->data.max_array_access);
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static bool
|
|
nir_constant_compare(const nir_constant *c1, const nir_constant *c2)
|
|
{
|
|
bool match = true;
|
|
|
|
match &= memcmp(c1->values, c2->values, sizeof(c1->values)) == 0;
|
|
match &= c1->is_null_constant == c2->is_null_constant;
|
|
match &= c1->num_elements == c2->num_elements;
|
|
if (!match)
|
|
return false;
|
|
|
|
for (unsigned i = 0; i < c1->num_elements; i++) {
|
|
match &= nir_constant_compare(c1->elements[i], c2->elements[i]);
|
|
}
|
|
|
|
return match;
|
|
}
|
|
|
|
struct ifc_var {
|
|
nir_shader *shader;
|
|
nir_variable *var;
|
|
};
|
|
|
|
/**
|
|
* Perform validation of global variables used across multiple shaders
|
|
*/
|
|
static void
|
|
cross_validate_globals(void *mem_ctx, const struct gl_constants *consts,
|
|
struct gl_shader_program *prog,
|
|
nir_shader *shader, struct hash_table *variables,
|
|
bool uniforms_only)
|
|
{
|
|
nir_foreach_variable_in_shader(var, shader) {
|
|
if (uniforms_only &&
|
|
(var->data.mode != nir_var_uniform &&
|
|
var->data.mode != nir_var_mem_ubo &&
|
|
var->data.mode != nir_var_image &&
|
|
var->data.mode != nir_var_mem_ssbo))
|
|
continue;
|
|
|
|
/* don't cross validate subroutine uniforms */
|
|
if (glsl_contains_subroutine(var->type))
|
|
continue;
|
|
|
|
/* Don't cross validate interface instances. These are only relevant
|
|
* inside a shader. The cross validation is done at the Interface Block
|
|
* name level.
|
|
*/
|
|
if (glsl_without_array(var->type) == var->interface_type)
|
|
continue;
|
|
|
|
/* Don't cross validate compiler temporaries that are at global scope.
|
|
* These will eventually get pulled into the shaders 'main'.
|
|
*/
|
|
if (var->data.mode == nir_var_shader_temp &&
|
|
var->data.how_declared == nir_var_hidden)
|
|
continue;
|
|
|
|
/* If a global with this name has already been seen, verify that the
|
|
* new instance has the same type. In addition, if the globals have
|
|
* initializers, the values of the initializers must be the same.
|
|
*/
|
|
struct hash_entry *entry =
|
|
_mesa_hash_table_search(variables, var->name);
|
|
if (entry != NULL) {
|
|
struct ifc_var *existing_ifc = (struct ifc_var *) entry->data;
|
|
nir_variable *existing = existing_ifc->var;
|
|
|
|
/* Check if types match. */
|
|
if (var->type != existing->type) {
|
|
if (!gl_nir_validate_intrastage_arrays(prog, var, existing,
|
|
existing_ifc->shader, true)) {
|
|
/* If it is an unsized array in a Shader Storage Block,
|
|
* two different shaders can access to different elements.
|
|
* Because of that, they might be converted to different
|
|
* sized arrays, then check that they are compatible but
|
|
* ignore the array size.
|
|
*/
|
|
if (!(var->data.mode == nir_var_mem_ssbo &&
|
|
var->data.from_ssbo_unsized_array &&
|
|
existing->data.mode == nir_var_mem_ssbo &&
|
|
existing->data.from_ssbo_unsized_array &&
|
|
glsl_get_gl_type(var->type) == glsl_get_gl_type(existing->type))) {
|
|
linker_error(prog, "%s `%s' declared as type "
|
|
"`%s' and type `%s'\n",
|
|
gl_nir_mode_string(var),
|
|
var->name, glsl_get_type_name(var->type),
|
|
glsl_get_type_name(existing->type));
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (var->data.explicit_location) {
|
|
if (existing->data.explicit_location
|
|
&& (var->data.location != existing->data.location)) {
|
|
linker_error(prog, "explicit locations for %s "
|
|
"`%s' have differing values\n",
|
|
gl_nir_mode_string(var), var->name);
|
|
return;
|
|
}
|
|
|
|
if (var->data.location_frac != existing->data.location_frac) {
|
|
linker_error(prog, "explicit components for %s `%s' have "
|
|
"differing values\n", gl_nir_mode_string(var),
|
|
var->name);
|
|
return;
|
|
}
|
|
|
|
existing->data.location = var->data.location;
|
|
existing->data.explicit_location = true;
|
|
} else {
|
|
/* Check if uniform with implicit location was marked explicit
|
|
* by earlier shader stage. If so, mark it explicit in this stage
|
|
* too to make sure later processing does not treat it as
|
|
* implicit one.
|
|
*/
|
|
if (existing->data.explicit_location) {
|
|
var->data.location = existing->data.location;
|
|
var->data.explicit_location = true;
|
|
}
|
|
}
|
|
|
|
/* From the GLSL 4.20 specification:
|
|
* "A link error will result if two compilation units in a program
|
|
* specify different integer-constant bindings for the same
|
|
* opaque-uniform name. However, it is not an error to specify a
|
|
* binding on some but not all declarations for the same name"
|
|
*/
|
|
if (var->data.explicit_binding) {
|
|
if (existing->data.explicit_binding &&
|
|
var->data.binding != existing->data.binding) {
|
|
linker_error(prog, "explicit bindings for %s "
|
|
"`%s' have differing values\n",
|
|
gl_nir_mode_string(var), var->name);
|
|
return;
|
|
}
|
|
|
|
existing->data.binding = var->data.binding;
|
|
existing->data.explicit_binding = true;
|
|
}
|
|
|
|
if (glsl_contains_atomic(var->type) &&
|
|
var->data.offset != existing->data.offset) {
|
|
linker_error(prog, "offset specifications for %s "
|
|
"`%s' have differing values\n",
|
|
gl_nir_mode_string(var), var->name);
|
|
return;
|
|
}
|
|
|
|
/* Validate layout qualifiers for gl_FragDepth.
|
|
*
|
|
* From the AMD/ARB_conservative_depth specs:
|
|
*
|
|
* "If gl_FragDepth is redeclared in any fragment shader in a
|
|
* program, it must be redeclared in all fragment shaders in
|
|
* that program that have static assignments to
|
|
* gl_FragDepth. All redeclarations of gl_FragDepth in all
|
|
* fragment shaders in a single program must have the same set
|
|
* of qualifiers."
|
|
*/
|
|
if (strcmp(var->name, "gl_FragDepth") == 0) {
|
|
bool layout_declared = var->data.depth_layout != nir_depth_layout_none;
|
|
bool layout_differs =
|
|
var->data.depth_layout != existing->data.depth_layout;
|
|
|
|
if (layout_declared && layout_differs) {
|
|
linker_error(prog,
|
|
"All redeclarations of gl_FragDepth in all "
|
|
"fragment shaders in a single program must have "
|
|
"the same set of qualifiers.\n");
|
|
}
|
|
|
|
if (var->data.used && layout_differs) {
|
|
linker_error(prog,
|
|
"If gl_FragDepth is redeclared with a layout "
|
|
"qualifier in any fragment shader, it must be "
|
|
"redeclared with the same layout qualifier in "
|
|
"all fragment shaders that have assignments to "
|
|
"gl_FragDepth\n");
|
|
}
|
|
}
|
|
|
|
/* Page 35 (page 41 of the PDF) of the GLSL 4.20 spec says:
|
|
*
|
|
* "If a shared global has multiple initializers, the
|
|
* initializers must all be constant expressions, and they
|
|
* must all have the same value. Otherwise, a link error will
|
|
* result. (A shared global having only one initializer does
|
|
* not require that initializer to be a constant expression.)"
|
|
*
|
|
* Previous to 4.20 the GLSL spec simply said that initializers
|
|
* must have the same value. In this case of non-constant
|
|
* initializers, this was impossible to determine. As a result,
|
|
* no vendor actually implemented that behavior. The 4.20
|
|
* behavior matches the implemented behavior of at least one other
|
|
* vendor, so we'll implement that for all GLSL versions.
|
|
* If (at least) one of these constant expressions is implicit,
|
|
* because it was added by glsl_zero_init, we skip the verification.
|
|
*/
|
|
if (var->constant_initializer != NULL) {
|
|
if (existing->constant_initializer != NULL &&
|
|
!existing->data.is_implicit_initializer &&
|
|
!var->data.is_implicit_initializer) {
|
|
if (!nir_constant_compare(var->constant_initializer,
|
|
existing->constant_initializer)) {
|
|
linker_error(prog, "initializers for %s "
|
|
"`%s' have differing values\n",
|
|
gl_nir_mode_string(var), var->name);
|
|
return;
|
|
}
|
|
} else {
|
|
/* If the first-seen instance of a particular uniform did
|
|
* not have an initializer but a later instance does,
|
|
* replace the former with the later.
|
|
*/
|
|
if (!var->data.is_implicit_initializer)
|
|
_mesa_hash_table_insert(variables, existing->name, var);
|
|
}
|
|
}
|
|
|
|
if (var->data.has_initializer) {
|
|
if (existing->data.has_initializer
|
|
&& (var->constant_initializer == NULL
|
|
|| existing->constant_initializer == NULL)) {
|
|
linker_error(prog,
|
|
"shared global variable `%s' has multiple "
|
|
"non-constant initializers.\n",
|
|
var->name);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (existing->data.explicit_invariant != var->data.explicit_invariant) {
|
|
linker_error(prog, "declarations for %s `%s' have "
|
|
"mismatching invariant qualifiers\n",
|
|
gl_nir_mode_string(var), var->name);
|
|
return;
|
|
}
|
|
if (existing->data.centroid != var->data.centroid) {
|
|
linker_error(prog, "declarations for %s `%s' have "
|
|
"mismatching centroid qualifiers\n",
|
|
gl_nir_mode_string(var), var->name);
|
|
return;
|
|
}
|
|
if (existing->data.sample != var->data.sample) {
|
|
linker_error(prog, "declarations for %s `%s` have "
|
|
"mismatching sample qualifiers\n",
|
|
gl_nir_mode_string(var), var->name);
|
|
return;
|
|
}
|
|
if (existing->data.image.format != var->data.image.format) {
|
|
linker_error(prog, "declarations for %s `%s` have "
|
|
"mismatching image format qualifiers\n",
|
|
gl_nir_mode_string(var), var->name);
|
|
return;
|
|
}
|
|
|
|
/* Check the precision qualifier matches for uniform variables on
|
|
* GLSL ES.
|
|
*/
|
|
if (!consts->AllowGLSLRelaxedES &&
|
|
prog->IsES && !var->interface_type &&
|
|
existing->data.precision != var->data.precision) {
|
|
if ((existing->data.used && var->data.used) ||
|
|
prog->GLSL_Version >= 300) {
|
|
linker_error(prog, "declarations for %s `%s` have "
|
|
"mismatching precision qualifiers\n",
|
|
gl_nir_mode_string(var), var->name);
|
|
return;
|
|
} else {
|
|
linker_warning(prog, "declarations for %s `%s` have "
|
|
"mismatching precision qualifiers\n",
|
|
gl_nir_mode_string(var), var->name);
|
|
}
|
|
}
|
|
|
|
/* In OpenGL GLSL 3.20 spec, section 4.3.9:
|
|
*
|
|
* "It is a link-time error if any particular shader interface
|
|
* contains:
|
|
*
|
|
* - two different blocks, each having no instance name, and each
|
|
* having a member of the same name, or
|
|
*
|
|
* - a variable outside a block, and a block with no instance name,
|
|
* where the variable has the same name as a member in the block."
|
|
*/
|
|
const glsl_type *var_itype = var->interface_type;
|
|
const glsl_type *existing_itype = existing->interface_type;
|
|
if (var_itype != existing_itype) {
|
|
if (!var_itype || !existing_itype) {
|
|
linker_error(prog, "declarations for %s `%s` are inside block "
|
|
"`%s` and outside a block",
|
|
gl_nir_mode_string(var), var->name,
|
|
glsl_get_type_name(var_itype ? var_itype : existing_itype));
|
|
return;
|
|
} else if (strcmp(glsl_get_type_name(var_itype), glsl_get_type_name(existing_itype)) != 0) {
|
|
linker_error(prog, "declarations for %s `%s` are inside blocks "
|
|
"`%s` and `%s`",
|
|
gl_nir_mode_string(var), var->name,
|
|
glsl_get_type_name(existing_itype),
|
|
glsl_get_type_name(var_itype));
|
|
return;
|
|
}
|
|
}
|
|
} else {
|
|
struct ifc_var *ifc_var = ralloc(mem_ctx, struct ifc_var);
|
|
ifc_var->var = var;
|
|
ifc_var->shader = shader;
|
|
_mesa_hash_table_insert(variables, var->name, ifc_var);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Perform validation of uniforms used across multiple shader stages
|
|
*/
|
|
static void
|
|
cross_validate_uniforms(const struct gl_constants *consts,
|
|
struct gl_shader_program *prog)
|
|
{
|
|
void *mem_ctx = ralloc_context(NULL);
|
|
struct hash_table *variables =
|
|
_mesa_hash_table_create(mem_ctx, _mesa_hash_string, _mesa_key_string_equal);
|
|
for (unsigned i = 0; i < MESA_SHADER_STAGES; i++) {
|
|
if (prog->_LinkedShaders[i] == NULL)
|
|
continue;
|
|
|
|
cross_validate_globals(mem_ctx, consts, prog,
|
|
prog->_LinkedShaders[i]->Program->nir,
|
|
variables, true);
|
|
}
|
|
|
|
ralloc_free(mem_ctx);
|
|
}
|
|
|
|
static bool
|
|
parameter_lists_match_exact(nir_parameter *params_a, nir_parameter *params_b,
|
|
unsigned num_params_a, unsigned num_params_b)
|
|
{
|
|
if (num_params_a != num_params_b)
|
|
return false;
|
|
|
|
for (unsigned i = 0; i < num_params_a; i++) {
|
|
if (params_a[i].type != params_b[i].type)
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool
|
|
exact_matching_signature(nir_function *other, nir_function *func)
|
|
{
|
|
return parameter_lists_match_exact(other->params, func->params,
|
|
other->num_params, func->num_params);
|
|
}
|
|
|
|
static bool
|
|
validate_xfb_buffer_stride(const struct gl_constants *consts, unsigned idx,
|
|
struct gl_shader_program *prog)
|
|
{
|
|
/* We will validate doubles at a later stage */
|
|
if (prog->TransformFeedback.BufferStride[idx] % 4) {
|
|
linker_error(prog, "invalid qualifier xfb_stride=%d must be a "
|
|
"multiple of 4 or if its applied to a type that is "
|
|
"or contains a double a multiple of 8.",
|
|
prog->TransformFeedback.BufferStride[idx]);
|
|
return false;
|
|
}
|
|
|
|
if (prog->TransformFeedback.BufferStride[idx] / 4 >
|
|
consts->MaxTransformFeedbackInterleavedComponents) {
|
|
linker_error(prog, "The MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS "
|
|
"limit has been exceeded.");
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Check for conflicting xfb_stride default qualifiers and store buffer stride
|
|
* for later use.
|
|
*/
|
|
static void
|
|
link_xfb_stride_layout_qualifiers(const struct gl_constants *consts,
|
|
struct gl_shader_program *prog,
|
|
struct gl_shader **shader_list,
|
|
unsigned num_shaders)
|
|
{
|
|
for (unsigned i = 0; i < MAX_FEEDBACK_BUFFERS; i++) {
|
|
prog->TransformFeedback.BufferStride[i] = 0;
|
|
}
|
|
|
|
for (unsigned i = 0; i < num_shaders; i++) {
|
|
struct gl_shader *shader = shader_list[i];
|
|
|
|
for (unsigned j = 0; j < MAX_FEEDBACK_BUFFERS; j++) {
|
|
if (shader->TransformFeedbackBufferStride[j]) {
|
|
if (prog->TransformFeedback.BufferStride[j] == 0) {
|
|
prog->TransformFeedback.BufferStride[j] =
|
|
shader->TransformFeedbackBufferStride[j];
|
|
if (!validate_xfb_buffer_stride(consts, j, prog))
|
|
return;
|
|
} else if (prog->TransformFeedback.BufferStride[j] !=
|
|
shader->TransformFeedbackBufferStride[j]){
|
|
linker_error(prog,
|
|
"intrastage shaders defined with conflicting "
|
|
"xfb_stride for buffer %d (%d and %d)\n", j,
|
|
prog->TransformFeedback.BufferStride[j],
|
|
shader->TransformFeedbackBufferStride[j]);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check for conflicting bindless/bound sampler/image layout qualifiers at
|
|
* global scope.
|
|
*/
|
|
static void
|
|
link_bindless_layout_qualifiers(struct gl_shader_program *prog,
|
|
struct gl_shader **shader_list,
|
|
unsigned num_shaders)
|
|
{
|
|
bool bindless_sampler, bindless_image;
|
|
bool bound_sampler, bound_image;
|
|
|
|
bindless_sampler = bindless_image = false;
|
|
bound_sampler = bound_image = false;
|
|
|
|
for (unsigned i = 0; i < num_shaders; i++) {
|
|
struct gl_shader *shader = shader_list[i];
|
|
|
|
if (shader->bindless_sampler)
|
|
bindless_sampler = true;
|
|
if (shader->bindless_image)
|
|
bindless_image = true;
|
|
if (shader->bound_sampler)
|
|
bound_sampler = true;
|
|
if (shader->bound_image)
|
|
bound_image = true;
|
|
|
|
if ((bindless_sampler && bound_sampler) ||
|
|
(bindless_image && bound_image)) {
|
|
/* From section 4.4.6 of the ARB_bindless_texture spec:
|
|
*
|
|
* "If both bindless_sampler and bound_sampler, or bindless_image
|
|
* and bound_image, are declared at global scope in any
|
|
* compilation unit, a link- time error will be generated."
|
|
*/
|
|
linker_error(prog, "both bindless_sampler and bound_sampler, or "
|
|
"bindless_image and bound_image, can't be declared at "
|
|
"global scope");
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check for conflicting viewport_relative settings across shaders, and sets
|
|
* the value for the linked shader.
|
|
*/
|
|
static void
|
|
link_layer_viewport_relative_qualifier(struct gl_shader_program *prog,
|
|
struct gl_program *gl_prog,
|
|
struct gl_shader **shader_list,
|
|
unsigned num_shaders)
|
|
{
|
|
unsigned i;
|
|
|
|
/* Find first shader with explicit layer declaration */
|
|
for (i = 0; i < num_shaders; i++) {
|
|
if (shader_list[i]->redeclares_gl_layer) {
|
|
gl_prog->nir->info.layer_viewport_relative =
|
|
shader_list[i]->layer_viewport_relative;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Now make sure that each subsequent shader's explicit layer declaration
|
|
* matches the first one's.
|
|
*/
|
|
for (; i < num_shaders; i++) {
|
|
if (shader_list[i]->redeclares_gl_layer &&
|
|
shader_list[i]->layer_viewport_relative !=
|
|
gl_prog->nir->info.layer_viewport_relative) {
|
|
linker_error(prog, "all gl_Layer redeclarations must have identical "
|
|
"viewport_relative settings");
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Performs the cross-validation of tessellation control shader vertices and
|
|
* layout qualifiers for the attached tessellation control shaders,
|
|
* and propagates them to the linked TCS and linked shader program.
|
|
*/
|
|
static void
|
|
link_tcs_out_layout_qualifiers(struct gl_shader_program *prog,
|
|
struct gl_program *gl_prog,
|
|
struct gl_shader **shader_list,
|
|
unsigned num_shaders)
|
|
{
|
|
if (gl_prog->info.stage != MESA_SHADER_TESS_CTRL)
|
|
return;
|
|
|
|
gl_prog->nir->info.tess.tcs_vertices_out = 0;
|
|
|
|
/* From the GLSL 4.0 spec (chapter 4.3.8.2):
|
|
*
|
|
* "All tessellation control shader layout declarations in a program
|
|
* must specify the same output patch vertex count. There must be at
|
|
* least one layout qualifier specifying an output patch vertex count
|
|
* in any program containing tessellation control shaders; however,
|
|
* such a declaration is not required in all tessellation control
|
|
* shaders."
|
|
*/
|
|
|
|
for (unsigned i = 0; i < num_shaders; i++) {
|
|
struct gl_shader *shader = shader_list[i];
|
|
|
|
if (shader->info.TessCtrl.VerticesOut != 0) {
|
|
if (gl_prog->nir->info.tess.tcs_vertices_out != 0 &&
|
|
gl_prog->nir->info.tess.tcs_vertices_out !=
|
|
(unsigned) shader->info.TessCtrl.VerticesOut) {
|
|
linker_error(prog, "tessellation control shader defined with "
|
|
"conflicting output vertex count (%d and %d)\n",
|
|
gl_prog->nir->info.tess.tcs_vertices_out,
|
|
shader->info.TessCtrl.VerticesOut);
|
|
return;
|
|
}
|
|
gl_prog->nir->info.tess.tcs_vertices_out =
|
|
shader->info.TessCtrl.VerticesOut;
|
|
}
|
|
}
|
|
|
|
/* Just do the intrastage -> interstage propagation right now,
|
|
* since we already know we're in the right type of shader program
|
|
* for doing it.
|
|
*/
|
|
if (gl_prog->nir->info.tess.tcs_vertices_out == 0) {
|
|
linker_error(prog, "tessellation control shader didn't declare "
|
|
"vertices out layout qualifier\n");
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Performs the cross-validation of tessellation evaluation shader
|
|
* primitive type, vertex spacing, ordering and point_mode layout qualifiers
|
|
* for the attached tessellation evaluation shaders, and propagates them
|
|
* to the linked TES and linked shader program.
|
|
*/
|
|
static void
|
|
link_tes_in_layout_qualifiers(struct gl_shader_program *prog,
|
|
struct gl_program *gl_prog,
|
|
struct gl_shader **shader_list,
|
|
unsigned num_shaders)
|
|
{
|
|
if (gl_prog->info.stage != MESA_SHADER_TESS_EVAL)
|
|
return;
|
|
|
|
int point_mode = -1;
|
|
unsigned vertex_order = 0;
|
|
|
|
gl_prog->nir->info.tess._primitive_mode = TESS_PRIMITIVE_UNSPECIFIED;
|
|
gl_prog->nir->info.tess.spacing = TESS_SPACING_UNSPECIFIED;
|
|
|
|
/* From the GLSL 4.0 spec (chapter 4.3.8.1):
|
|
*
|
|
* "At least one tessellation evaluation shader (compilation unit) in
|
|
* a program must declare a primitive mode in its input layout.
|
|
* Declaration vertex spacing, ordering, and point mode identifiers is
|
|
* optional. It is not required that all tessellation evaluation
|
|
* shaders in a program declare a primitive mode. If spacing or
|
|
* vertex ordering declarations are omitted, the tessellation
|
|
* primitive generator will use equal spacing or counter-clockwise
|
|
* vertex ordering, respectively. If a point mode declaration is
|
|
* omitted, the tessellation primitive generator will produce lines or
|
|
* triangles according to the primitive mode."
|
|
*/
|
|
|
|
for (unsigned i = 0; i < num_shaders; i++) {
|
|
struct gl_shader *shader = shader_list[i];
|
|
|
|
if (shader->info.TessEval._PrimitiveMode != TESS_PRIMITIVE_UNSPECIFIED) {
|
|
if (gl_prog->nir->info.tess._primitive_mode != TESS_PRIMITIVE_UNSPECIFIED &&
|
|
gl_prog->nir->info.tess._primitive_mode !=
|
|
shader->info.TessEval._PrimitiveMode) {
|
|
linker_error(prog, "tessellation evaluation shader defined with "
|
|
"conflicting input primitive modes.\n");
|
|
return;
|
|
}
|
|
gl_prog->nir->info.tess._primitive_mode =
|
|
shader->info.TessEval._PrimitiveMode;
|
|
}
|
|
|
|
if (shader->info.TessEval.Spacing != 0) {
|
|
if (gl_prog->nir->info.tess.spacing != 0 &&
|
|
gl_prog->nir->info.tess.spacing != shader->info.TessEval.Spacing) {
|
|
linker_error(prog, "tessellation evaluation shader defined with "
|
|
"conflicting vertex spacing.\n");
|
|
return;
|
|
}
|
|
gl_prog->nir->info.tess.spacing = shader->info.TessEval.Spacing;
|
|
}
|
|
|
|
if (shader->info.TessEval.VertexOrder != 0) {
|
|
if (vertex_order != 0 &&
|
|
vertex_order != shader->info.TessEval.VertexOrder) {
|
|
linker_error(prog, "tessellation evaluation shader defined with "
|
|
"conflicting ordering.\n");
|
|
return;
|
|
}
|
|
vertex_order = shader->info.TessEval.VertexOrder;
|
|
}
|
|
|
|
if (shader->info.TessEval.PointMode != -1) {
|
|
if (point_mode != -1 &&
|
|
point_mode != shader->info.TessEval.PointMode) {
|
|
linker_error(prog, "tessellation evaluation shader defined with "
|
|
"conflicting point modes.\n");
|
|
return;
|
|
}
|
|
point_mode = shader->info.TessEval.PointMode;
|
|
}
|
|
|
|
}
|
|
|
|
/* Just do the intrastage -> interstage propagation right now,
|
|
* since we already know we're in the right type of shader program
|
|
* for doing it.
|
|
*/
|
|
if (gl_prog->nir->info.tess._primitive_mode == TESS_PRIMITIVE_UNSPECIFIED) {
|
|
linker_error(prog,
|
|
"tessellation evaluation shader didn't declare input "
|
|
"primitive modes.\n");
|
|
return;
|
|
}
|
|
|
|
if (gl_prog->nir->info.tess.spacing == TESS_SPACING_UNSPECIFIED)
|
|
gl_prog->nir->info.tess.spacing = TESS_SPACING_EQUAL;
|
|
|
|
if (vertex_order == 0 || vertex_order == GL_CCW)
|
|
gl_prog->nir->info.tess.ccw = true;
|
|
else
|
|
gl_prog->nir->info.tess.ccw = false;
|
|
|
|
|
|
if (point_mode == -1 || point_mode == GL_FALSE)
|
|
gl_prog->nir->info.tess.point_mode = false;
|
|
else
|
|
gl_prog->nir->info.tess.point_mode = true;
|
|
}
|
|
|
|
|
|
/**
|
|
* Performs the cross-validation of layout qualifiers specified in
|
|
* redeclaration of gl_FragCoord for the attached fragment shaders,
|
|
* and propagates them to the linked FS and linked shader program.
|
|
*/
|
|
static void
|
|
link_fs_inout_layout_qualifiers(struct gl_shader_program *prog,
|
|
struct gl_linked_shader *linked_shader,
|
|
struct gl_shader **shader_list,
|
|
unsigned num_shaders,
|
|
bool arb_fragment_coord_conventions_enable)
|
|
{
|
|
bool redeclares_gl_fragcoord = false;
|
|
bool uses_gl_fragcoord = false;
|
|
bool origin_upper_left = false;
|
|
bool pixel_center_integer = false;
|
|
|
|
if (linked_shader->Stage != MESA_SHADER_FRAGMENT ||
|
|
(prog->GLSL_Version < 150 && !arb_fragment_coord_conventions_enable))
|
|
return;
|
|
|
|
for (unsigned i = 0; i < num_shaders; i++) {
|
|
struct gl_shader *shader = shader_list[i];
|
|
/* From the GLSL 1.50 spec, page 39:
|
|
*
|
|
* "If gl_FragCoord is redeclared in any fragment shader in a program,
|
|
* it must be redeclared in all the fragment shaders in that program
|
|
* that have a static use gl_FragCoord."
|
|
*/
|
|
if ((redeclares_gl_fragcoord && !shader->redeclares_gl_fragcoord &&
|
|
shader->uses_gl_fragcoord)
|
|
|| (shader->redeclares_gl_fragcoord && !redeclares_gl_fragcoord &&
|
|
uses_gl_fragcoord)) {
|
|
linker_error(prog, "fragment shader defined with conflicting "
|
|
"layout qualifiers for gl_FragCoord\n");
|
|
}
|
|
|
|
/* From the GLSL 1.50 spec, page 39:
|
|
*
|
|
* "All redeclarations of gl_FragCoord in all fragment shaders in a
|
|
* single program must have the same set of qualifiers."
|
|
*/
|
|
if (redeclares_gl_fragcoord && shader->redeclares_gl_fragcoord &&
|
|
(shader->origin_upper_left != origin_upper_left ||
|
|
shader->pixel_center_integer != pixel_center_integer)) {
|
|
linker_error(prog, "fragment shader defined with conflicting "
|
|
"layout qualifiers for gl_FragCoord\n");
|
|
}
|
|
|
|
/* Update the linked shader state. Note that uses_gl_fragcoord should
|
|
* accumulate the results. The other values should replace. If there
|
|
* are multiple redeclarations, all the fields except uses_gl_fragcoord
|
|
* are already known to be the same.
|
|
*/
|
|
if (shader->redeclares_gl_fragcoord || shader->uses_gl_fragcoord) {
|
|
redeclares_gl_fragcoord = shader->redeclares_gl_fragcoord;
|
|
uses_gl_fragcoord |= shader->uses_gl_fragcoord;
|
|
origin_upper_left = shader->origin_upper_left;
|
|
pixel_center_integer = shader->pixel_center_integer;
|
|
}
|
|
|
|
linked_shader->Program->nir->info.fs.early_fragment_tests |=
|
|
shader->EarlyFragmentTests || shader->PostDepthCoverage;
|
|
linked_shader->Program->nir->info.fs.inner_coverage |= shader->InnerCoverage;
|
|
linked_shader->Program->nir->info.fs.post_depth_coverage |=
|
|
shader->PostDepthCoverage;
|
|
linked_shader->Program->nir->info.fs.pixel_interlock_ordered |=
|
|
shader->PixelInterlockOrdered;
|
|
linked_shader->Program->nir->info.fs.pixel_interlock_unordered |=
|
|
shader->PixelInterlockUnordered;
|
|
linked_shader->Program->nir->info.fs.sample_interlock_ordered |=
|
|
shader->SampleInterlockOrdered;
|
|
linked_shader->Program->nir->info.fs.sample_interlock_unordered |=
|
|
shader->SampleInterlockUnordered;
|
|
linked_shader->Program->nir->info.fs.advanced_blend_modes |= shader->BlendSupport;
|
|
}
|
|
|
|
linked_shader->Program->nir->info.fs.pixel_center_integer = pixel_center_integer;
|
|
linked_shader->Program->nir->info.fs.origin_upper_left = origin_upper_left;
|
|
}
|
|
|
|
/**
|
|
* Performs the cross-validation of geometry shader max_vertices and
|
|
* primitive type layout qualifiers for the attached geometry shaders,
|
|
* and propagates them to the linked GS and linked shader program.
|
|
*/
|
|
static void
|
|
link_gs_inout_layout_qualifiers(struct gl_shader_program *prog,
|
|
struct gl_program *gl_prog,
|
|
struct gl_shader **shader_list,
|
|
unsigned num_shaders)
|
|
{
|
|
/* No in/out qualifiers defined for anything but GLSL 1.50+
|
|
* geometry shaders so far.
|
|
*/
|
|
if (gl_prog->info.stage != MESA_SHADER_GEOMETRY || prog->GLSL_Version < 150)
|
|
return;
|
|
|
|
int vertices_out = -1;
|
|
|
|
gl_prog->nir->info.gs.invocations = 0;
|
|
gl_prog->nir->info.gs.input_primitive = MESA_PRIM_UNKNOWN;
|
|
gl_prog->nir->info.gs.output_primitive = MESA_PRIM_UNKNOWN;
|
|
|
|
/* From the GLSL 1.50 spec, page 46:
|
|
*
|
|
* "All geometry shader output layout declarations in a program
|
|
* must declare the same layout and same value for
|
|
* max_vertices. There must be at least one geometry output
|
|
* layout declaration somewhere in a program, but not all
|
|
* geometry shaders (compilation units) are required to
|
|
* declare it."
|
|
*/
|
|
|
|
for (unsigned i = 0; i < num_shaders; i++) {
|
|
struct gl_shader *shader = shader_list[i];
|
|
|
|
if (shader->info.Geom.InputType != MESA_PRIM_UNKNOWN) {
|
|
if (gl_prog->nir->info.gs.input_primitive != MESA_PRIM_UNKNOWN &&
|
|
gl_prog->nir->info.gs.input_primitive !=
|
|
shader->info.Geom.InputType) {
|
|
linker_error(prog, "geometry shader defined with conflicting "
|
|
"input types\n");
|
|
return;
|
|
}
|
|
gl_prog->nir->info.gs.input_primitive =
|
|
(enum mesa_prim)shader->info.Geom.InputType;
|
|
}
|
|
|
|
if (shader->info.Geom.OutputType != MESA_PRIM_UNKNOWN) {
|
|
if (gl_prog->nir->info.gs.output_primitive != MESA_PRIM_UNKNOWN &&
|
|
gl_prog->nir->info.gs.output_primitive !=
|
|
shader->info.Geom.OutputType) {
|
|
linker_error(prog, "geometry shader defined with conflicting "
|
|
"output types\n");
|
|
return;
|
|
}
|
|
gl_prog->nir->info.gs.output_primitive =
|
|
(enum mesa_prim)shader->info.Geom.OutputType;
|
|
}
|
|
|
|
if (shader->info.Geom.VerticesOut != -1) {
|
|
if (vertices_out != -1 &&
|
|
vertices_out != shader->info.Geom.VerticesOut) {
|
|
linker_error(prog, "geometry shader defined with conflicting "
|
|
"output vertex count (%d and %d)\n",
|
|
vertices_out, shader->info.Geom.VerticesOut);
|
|
return;
|
|
}
|
|
vertices_out = shader->info.Geom.VerticesOut;
|
|
}
|
|
|
|
if (shader->info.Geom.Invocations != 0) {
|
|
if (gl_prog->nir->info.gs.invocations != 0 &&
|
|
gl_prog->nir->info.gs.invocations !=
|
|
(unsigned) shader->info.Geom.Invocations) {
|
|
linker_error(prog, "geometry shader defined with conflicting "
|
|
"invocation count (%d and %d)\n",
|
|
gl_prog->nir->info.gs.invocations,
|
|
shader->info.Geom.Invocations);
|
|
return;
|
|
}
|
|
gl_prog->nir->info.gs.invocations = shader->info.Geom.Invocations;
|
|
}
|
|
}
|
|
|
|
/* Just do the intrastage -> interstage propagation right now,
|
|
* since we already know we're in the right type of shader program
|
|
* for doing it.
|
|
*/
|
|
if (gl_prog->nir->info.gs.input_primitive == MESA_PRIM_UNKNOWN) {
|
|
linker_error(prog,
|
|
"geometry shader didn't declare primitive input type\n");
|
|
return;
|
|
}
|
|
|
|
if (gl_prog->nir->info.gs.output_primitive == MESA_PRIM_UNKNOWN) {
|
|
linker_error(prog,
|
|
"geometry shader didn't declare primitive output type\n");
|
|
return;
|
|
}
|
|
|
|
if (vertices_out == -1) {
|
|
linker_error(prog,
|
|
"geometry shader didn't declare max_vertices\n");
|
|
return;
|
|
} else {
|
|
gl_prog->nir->info.gs.vertices_out = vertices_out;
|
|
}
|
|
|
|
if (gl_prog->nir->info.gs.invocations == 0)
|
|
gl_prog->nir->info.gs.invocations = 1;
|
|
}
|
|
|
|
|
|
/**
|
|
* Perform cross-validation of compute shader local_size_{x,y,z} layout and
|
|
* derivative arrangement qualifiers for the attached compute shaders, and
|
|
* propagate them to the linked CS and linked shader program.
|
|
*/
|
|
static void
|
|
link_cs_input_layout_qualifiers(struct gl_shader_program *prog,
|
|
struct gl_program *gl_prog,
|
|
struct gl_shader **shader_list,
|
|
unsigned num_shaders)
|
|
{
|
|
/* This function is called for all shader stages, but it only has an effect
|
|
* for compute shaders.
|
|
*/
|
|
if (gl_prog->info.stage != MESA_SHADER_COMPUTE)
|
|
return;
|
|
|
|
for (int i = 0; i < 3; i++)
|
|
gl_prog->nir->info.workgroup_size[i] = 0;
|
|
|
|
gl_prog->nir->info.workgroup_size_variable = false;
|
|
|
|
gl_prog->nir->info.derivative_group = DERIVATIVE_GROUP_NONE;
|
|
|
|
/* From the ARB_compute_shader spec, in the section describing local size
|
|
* declarations:
|
|
*
|
|
* If multiple compute shaders attached to a single program object
|
|
* declare local work-group size, the declarations must be identical;
|
|
* otherwise a link-time error results. Furthermore, if a program
|
|
* object contains any compute shaders, at least one must contain an
|
|
* input layout qualifier specifying the local work sizes of the
|
|
* program, or a link-time error will occur.
|
|
*/
|
|
for (unsigned sh = 0; sh < num_shaders; sh++) {
|
|
struct gl_shader *shader = shader_list[sh];
|
|
|
|
if (shader->info.Comp.LocalSize[0] != 0) {
|
|
if (gl_prog->nir->info.workgroup_size[0] != 0) {
|
|
for (int i = 0; i < 3; i++) {
|
|
if (gl_prog->nir->info.workgroup_size[i] !=
|
|
shader->info.Comp.LocalSize[i]) {
|
|
linker_error(prog, "compute shader defined with conflicting "
|
|
"local sizes\n");
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
for (int i = 0; i < 3; i++) {
|
|
gl_prog->nir->info.workgroup_size[i] =
|
|
shader->info.Comp.LocalSize[i];
|
|
}
|
|
} else if (shader->info.Comp.LocalSizeVariable) {
|
|
if (gl_prog->nir->info.workgroup_size[0] != 0) {
|
|
/* The ARB_compute_variable_group_size spec says:
|
|
*
|
|
* If one compute shader attached to a program declares a
|
|
* variable local group size and a second compute shader
|
|
* attached to the same program declares a fixed local group
|
|
* size, a link-time error results.
|
|
*/
|
|
linker_error(prog, "compute shader defined with both fixed and "
|
|
"variable local group size\n");
|
|
return;
|
|
}
|
|
gl_prog->nir->info.workgroup_size_variable = true;
|
|
}
|
|
|
|
enum gl_derivative_group group = shader->info.Comp.DerivativeGroup;
|
|
if (group != DERIVATIVE_GROUP_NONE) {
|
|
if (gl_prog->nir->info.derivative_group != DERIVATIVE_GROUP_NONE &&
|
|
gl_prog->nir->info.derivative_group != group) {
|
|
linker_error(prog, "compute shader defined with conflicting "
|
|
"derivative groups\n");
|
|
return;
|
|
}
|
|
gl_prog->nir->info.derivative_group = group;
|
|
}
|
|
}
|
|
|
|
/* Just do the intrastage -> interstage propagation right now,
|
|
* since we already know we're in the right type of shader program
|
|
* for doing it.
|
|
*/
|
|
if (gl_prog->nir->info.workgroup_size[0] == 0 &&
|
|
!gl_prog->nir->info.workgroup_size_variable) {
|
|
linker_error(prog, "compute shader must contain a fixed or a variable "
|
|
"local group size\n");
|
|
return;
|
|
}
|
|
|
|
if (gl_prog->nir->info.derivative_group == DERIVATIVE_GROUP_QUADS) {
|
|
if (gl_prog->nir->info.workgroup_size[0] % 2 != 0) {
|
|
linker_error(prog, "derivative_group_quadsNV must be used with a "
|
|
"local group size whose first dimension "
|
|
"is a multiple of 2\n");
|
|
return;
|
|
}
|
|
if (gl_prog->nir->info.workgroup_size[1] % 2 != 0) {
|
|
linker_error(prog, "derivative_group_quadsNV must be used with a local"
|
|
"group size whose second dimension "
|
|
"is a multiple of 2\n");
|
|
return;
|
|
}
|
|
} else if (gl_prog->nir->info.derivative_group == DERIVATIVE_GROUP_LINEAR) {
|
|
if ((gl_prog->nir->info.workgroup_size[0] *
|
|
gl_prog->nir->info.workgroup_size[1] *
|
|
gl_prog->nir->info.workgroup_size[2]) % 4 != 0) {
|
|
linker_error(prog, "derivative_group_linearNV must be used with a "
|
|
"local group size whose total number of invocations "
|
|
"is a multiple of 4\n");
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Combine a group of shaders for a single stage to generate a linked shader
|
|
*
|
|
* \note
|
|
* If this function is supplied a single shader, it is cloned, and the new
|
|
* shader is returned.
|
|
*/
|
|
static struct gl_linked_shader *
|
|
link_intrastage_shaders(void *mem_ctx,
|
|
struct gl_context *ctx,
|
|
struct gl_shader_program *prog,
|
|
struct gl_shader **shader_list,
|
|
unsigned num_shaders)
|
|
{
|
|
bool arb_fragment_coord_conventions_enable = false;
|
|
bool KHR_shader_subgroup_basic_enable = false;
|
|
unsigned view_mask = 0;
|
|
|
|
/* Check that global variables defined in multiple shaders are consistent.
|
|
*/
|
|
struct hash_table *variables =
|
|
_mesa_hash_table_create(mem_ctx, _mesa_hash_string, _mesa_key_string_equal);
|
|
for (unsigned i = 0; i < num_shaders; i++) {
|
|
if (shader_list[i] == NULL)
|
|
continue;
|
|
cross_validate_globals(mem_ctx, &ctx->Const, prog, shader_list[i]->nir,
|
|
variables, false);
|
|
if (shader_list[i]->ARB_fragment_coord_conventions_enable)
|
|
arb_fragment_coord_conventions_enable = true;
|
|
if (shader_list[i]->KHR_shader_subgroup_basic_enable)
|
|
KHR_shader_subgroup_basic_enable = true;
|
|
|
|
if (shader_list[i]->view_mask != 0) {
|
|
if (view_mask != 0 && shader_list[i]->view_mask != view_mask) {
|
|
linker_error(prog, "vertex shader defined with "
|
|
"conflicting num_views (%d and %d)\n",
|
|
ffs(view_mask) - 1, ffs(shader_list[i]->view_mask) - 1);
|
|
return NULL;
|
|
}
|
|
|
|
view_mask = shader_list[i]->view_mask;
|
|
}
|
|
}
|
|
|
|
if (!prog->data->LinkStatus)
|
|
return NULL;
|
|
|
|
/* Check that interface blocks defined in multiple shaders are consistent.
|
|
*/
|
|
gl_nir_validate_intrastage_interface_blocks(prog,
|
|
(const struct gl_shader **)shader_list,
|
|
num_shaders);
|
|
if (!prog->data->LinkStatus)
|
|
return NULL;
|
|
|
|
/* Check that there is only a single definition of each function signature
|
|
* across all shaders.
|
|
*/
|
|
for (unsigned i = 0; i < (num_shaders - 1); i++) {
|
|
nir_foreach_function_impl(func, shader_list[i]->nir) {
|
|
for (unsigned j = i + 1; j < num_shaders; j++) {
|
|
nir_function *other =
|
|
nir_shader_get_function_for_name(shader_list[j]->nir,
|
|
func->function->name);
|
|
|
|
/* If the other shader has no function (and therefore no function
|
|
* signatures) with the same name, skip to the next shader.
|
|
*/
|
|
if (other == NULL || other->impl == NULL)
|
|
continue;
|
|
|
|
bool exact_match =
|
|
exact_matching_signature(other, func->function);
|
|
|
|
if (exact_match) {
|
|
linker_error(prog, "function `%s' is multiply defined\n",
|
|
func->function->name);
|
|
return NULL;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Find the shader that defines main, and make a clone of it.
|
|
*
|
|
* Starting with the clone, search for undefined references. If one is
|
|
* found, find the shader that defines it. Clone the reference and add
|
|
* it to the shader. Repeat until there are no undefined references or
|
|
* until a reference cannot be resolved.
|
|
*/
|
|
struct gl_shader *main = NULL;
|
|
nir_function_impl *main_func = NULL;
|
|
for (unsigned i = 0; i < num_shaders; i++) {
|
|
main_func = nir_shader_get_entrypoint(shader_list[i]->nir);
|
|
if (main_func) {
|
|
main = shader_list[i];
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (main == NULL) {
|
|
linker_error(prog, "%s shader lacks `main'\n",
|
|
_mesa_shader_stage_to_string(shader_list[0]->Stage));
|
|
return NULL;
|
|
}
|
|
|
|
struct gl_linked_shader *linked = rzalloc(NULL, struct gl_linked_shader);
|
|
linked->Stage = shader_list[0]->Stage;
|
|
|
|
/* Create program and attach it to the linked shader */
|
|
struct gl_program *gl_prog =
|
|
ctx->Driver.NewProgram(ctx, shader_list[0]->Stage, prog->Name, false);
|
|
if (!gl_prog) {
|
|
prog->data->LinkStatus = LINKING_FAILURE;
|
|
_mesa_delete_linked_shader(ctx, linked);
|
|
return NULL;
|
|
}
|
|
|
|
_mesa_reference_shader_program_data(&gl_prog->sh.data, prog->data);
|
|
|
|
/* Don't use _mesa_reference_program() just take ownership */
|
|
linked->Program = gl_prog;
|
|
|
|
linked->Program->nir = nir_shader_clone(NULL, main->nir);
|
|
|
|
link_fs_inout_layout_qualifiers(prog, linked, shader_list, num_shaders,
|
|
arb_fragment_coord_conventions_enable);
|
|
link_tcs_out_layout_qualifiers(prog, gl_prog, shader_list, num_shaders);
|
|
link_tes_in_layout_qualifiers(prog, gl_prog, shader_list, num_shaders);
|
|
link_gs_inout_layout_qualifiers(prog, gl_prog, shader_list, num_shaders);
|
|
link_cs_input_layout_qualifiers(prog, gl_prog, shader_list, num_shaders);
|
|
|
|
if (linked->Stage != MESA_SHADER_FRAGMENT)
|
|
link_xfb_stride_layout_qualifiers(&ctx->Const, prog, shader_list, num_shaders);
|
|
|
|
link_bindless_layout_qualifiers(prog, shader_list, num_shaders);
|
|
|
|
link_layer_viewport_relative_qualifier(prog, gl_prog, shader_list, num_shaders);
|
|
|
|
gl_prog->nir->info.view_mask = view_mask;
|
|
gl_prog->nir->info.subgroup_size = KHR_shader_subgroup_basic_enable ?
|
|
SUBGROUP_SIZE_API_CONSTANT : SUBGROUP_SIZE_UNIFORM;
|
|
|
|
/* Move any instructions other than variable declarations or function
|
|
* declarations into main.
|
|
*/
|
|
if (!gl_nir_link_function_calls(prog, main, linked, shader_list, num_shaders)) {
|
|
_mesa_delete_linked_shader(ctx, linked);
|
|
return NULL;
|
|
}
|
|
|
|
/* Add calls to temp global instruction wrapper functions */
|
|
main_func = nir_shader_get_entrypoint(linked->Program->nir);
|
|
nir_builder b = nir_builder_create(main_func);
|
|
nir_foreach_function_impl(impl, linked->Program->nir) {
|
|
if (strncmp(impl->function->name, "gl_mesa_tmp", 11) == 0) {
|
|
nir_call_instr *call = nir_call_instr_create(linked->Program->nir,
|
|
impl->function);
|
|
b.cursor = nir_before_block(nir_start_block(main_func));
|
|
nir_builder_instr_insert(&b, &call->instr);
|
|
}
|
|
}
|
|
|
|
/* Make a pass over all variable declarations to ensure that arrays with
|
|
* unspecified sizes have a size specified. The size is inferred from the
|
|
* max_array_access field.
|
|
*/
|
|
gl_nir_linker_size_arrays(linked->Program->nir);
|
|
nir_fixup_deref_types(linked->Program->nir);
|
|
|
|
/* Now that we know the sizes of all the arrays, we can replace .length()
|
|
* calls with a constant expression.
|
|
*/
|
|
array_length_to_const(linked->Program->nir);
|
|
|
|
if (!prog->data->LinkStatus) {
|
|
_mesa_delete_linked_shader(ctx, linked);
|
|
return NULL;
|
|
}
|
|
|
|
/* At this point linked should contain all of the linked IR, so
|
|
* validate it to make sure nothing went wrong.
|
|
*/
|
|
nir_validate_shader(linked->Program->nir, "post shader stage combine");
|
|
|
|
lower_derivatives_without_layout(&b);
|
|
|
|
/* Set the linked source BLAKE3. */
|
|
if (num_shaders == 1) {
|
|
memcpy(linked->Program->nir->info.source_blake3,
|
|
shader_list[0]->compiled_source_blake3,
|
|
BLAKE3_OUT_LEN);
|
|
} else {
|
|
struct mesa_blake3 blake3_ctx;
|
|
_mesa_blake3_init(&blake3_ctx);
|
|
|
|
for (unsigned i = 0; i < num_shaders; i++) {
|
|
if (shader_list[i] == NULL)
|
|
continue;
|
|
|
|
_mesa_blake3_update(&blake3_ctx, shader_list[i]->compiled_source_blake3,
|
|
BLAKE3_OUT_LEN);
|
|
}
|
|
_mesa_blake3_final(&blake3_ctx, linked->Program->nir->info.source_blake3);
|
|
}
|
|
|
|
return linked;
|
|
}
|
|
|
|
/**
|
|
* Initializes explicit location slots to INACTIVE_UNIFORM_EXPLICIT_LOCATION
|
|
* for a variable, checks for overlaps between other uniforms using explicit
|
|
* locations.
|
|
*/
|
|
static int
|
|
reserve_explicit_locations(struct gl_shader_program *prog,
|
|
struct string_to_uint_map *map, nir_variable *var)
|
|
{
|
|
unsigned slots = glsl_type_uniform_locations(var->type);
|
|
unsigned max_loc = var->data.location + slots - 1;
|
|
unsigned return_value = slots;
|
|
|
|
/* Resize remap table if locations do not fit in the current one. */
|
|
if (max_loc + 1 > prog->NumUniformRemapTable) {
|
|
prog->UniformRemapTable =
|
|
reralloc(prog, prog->UniformRemapTable,
|
|
struct gl_uniform_storage *,
|
|
max_loc + 1);
|
|
|
|
if (!prog->UniformRemapTable) {
|
|
linker_error(prog, "Out of memory during linking.\n");
|
|
return -1;
|
|
}
|
|
|
|
/* Initialize allocated space. */
|
|
for (unsigned i = prog->NumUniformRemapTable; i < max_loc + 1; i++)
|
|
prog->UniformRemapTable[i] = NULL;
|
|
|
|
prog->NumUniformRemapTable = max_loc + 1;
|
|
}
|
|
|
|
for (unsigned i = 0; i < slots; i++) {
|
|
unsigned loc = var->data.location + i;
|
|
|
|
/* Check if location is already used. */
|
|
if (prog->UniformRemapTable[loc] == INACTIVE_UNIFORM_EXPLICIT_LOCATION) {
|
|
|
|
/* Possibly same uniform from a different stage, this is ok. */
|
|
unsigned hash_loc;
|
|
if (string_to_uint_map_get(map, &hash_loc, var->name) &&
|
|
hash_loc == loc - i) {
|
|
return_value = 0;
|
|
continue;
|
|
}
|
|
|
|
/* ARB_explicit_uniform_location specification states:
|
|
*
|
|
* "No two default-block uniform variables in the program can have
|
|
* the same location, even if they are unused, otherwise a compiler
|
|
* or linker error will be generated."
|
|
*/
|
|
linker_error(prog,
|
|
"location qualifier for uniform %s overlaps "
|
|
"previously used location\n",
|
|
var->name);
|
|
return -1;
|
|
}
|
|
|
|
/* Initialize location as inactive before optimization
|
|
* rounds and location assignment.
|
|
*/
|
|
prog->UniformRemapTable[loc] = INACTIVE_UNIFORM_EXPLICIT_LOCATION;
|
|
}
|
|
|
|
/* Note, base location used for arrays. */
|
|
string_to_uint_map_put(map, var->data.location, var->name);
|
|
|
|
return return_value;
|
|
}
|
|
|
|
static bool
|
|
reserve_subroutine_explicit_locations(struct gl_shader_program *prog,
|
|
struct gl_program *p,
|
|
nir_variable *var)
|
|
{
|
|
unsigned slots = glsl_type_uniform_locations(var->type);
|
|
unsigned max_loc = var->data.location + slots - 1;
|
|
|
|
/* Resize remap table if locations do not fit in the current one. */
|
|
if (max_loc + 1 > p->sh.NumSubroutineUniformRemapTable) {
|
|
p->sh.SubroutineUniformRemapTable =
|
|
reralloc(p, p->sh.SubroutineUniformRemapTable,
|
|
struct gl_uniform_storage *,
|
|
max_loc + 1);
|
|
|
|
if (!p->sh.SubroutineUniformRemapTable) {
|
|
linker_error(prog, "Out of memory during linking.\n");
|
|
return false;
|
|
}
|
|
|
|
/* Initialize allocated space. */
|
|
for (unsigned i = p->sh.NumSubroutineUniformRemapTable; i < max_loc + 1; i++)
|
|
p->sh.SubroutineUniformRemapTable[i] = NULL;
|
|
|
|
p->sh.NumSubroutineUniformRemapTable = max_loc + 1;
|
|
}
|
|
|
|
for (unsigned i = 0; i < slots; i++) {
|
|
unsigned loc = var->data.location + i;
|
|
|
|
/* Check if location is already used. */
|
|
if (p->sh.SubroutineUniformRemapTable[loc] == INACTIVE_UNIFORM_EXPLICIT_LOCATION) {
|
|
|
|
/* ARB_explicit_uniform_location specification states:
|
|
* "No two subroutine uniform variables can have the same location
|
|
* in the same shader stage, otherwise a compiler or linker error
|
|
* will be generated."
|
|
*/
|
|
linker_error(prog,
|
|
"location qualifier for uniform %s overlaps "
|
|
"previously used location\n",
|
|
var->name);
|
|
return false;
|
|
}
|
|
|
|
/* Initialize location as inactive before optimization
|
|
* rounds and location assignment.
|
|
*/
|
|
p->sh.SubroutineUniformRemapTable[loc] = INACTIVE_UNIFORM_EXPLICIT_LOCATION;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
/**
|
|
* Check and reserve all explicit uniform locations, called before
|
|
* any optimizations happen to handle also inactive uniforms and
|
|
* inactive array elements that may get trimmed away.
|
|
*/
|
|
static void
|
|
check_explicit_uniform_locations(const struct gl_extensions *exts,
|
|
struct gl_shader_program *prog)
|
|
{
|
|
prog->NumExplicitUniformLocations = 0;
|
|
|
|
if (!exts->ARB_explicit_uniform_location)
|
|
return;
|
|
|
|
/* This map is used to detect if overlapping explicit locations
|
|
* occur with the same uniform (from different stage) or a different one.
|
|
*/
|
|
struct string_to_uint_map *uniform_map = string_to_uint_map_ctor();
|
|
|
|
if (!uniform_map) {
|
|
linker_error(prog, "Out of memory during linking.\n");
|
|
return;
|
|
}
|
|
|
|
unsigned entries_total = 0;
|
|
unsigned mask = prog->data->linked_stages;
|
|
while (mask) {
|
|
const int i = u_bit_scan(&mask);
|
|
struct gl_program *p = prog->_LinkedShaders[i]->Program;
|
|
|
|
unsigned modes = nir_var_uniform | nir_var_mem_ubo | nir_var_image;
|
|
nir_foreach_variable_with_modes(var, p->nir, modes) {
|
|
if (var->data.explicit_location) {
|
|
bool ret = false;
|
|
if (glsl_type_is_subroutine(glsl_without_array(var->type)))
|
|
ret = reserve_subroutine_explicit_locations(prog, p, var);
|
|
else {
|
|
int slots = reserve_explicit_locations(prog, uniform_map,
|
|
var);
|
|
if (slots != -1) {
|
|
ret = true;
|
|
entries_total += slots;
|
|
}
|
|
}
|
|
if (!ret) {
|
|
string_to_uint_map_dtor(uniform_map);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
link_util_update_empty_uniform_locations(prog);
|
|
|
|
string_to_uint_map_dtor(uniform_map);
|
|
prog->NumExplicitUniformLocations = entries_total;
|
|
}
|
|
|
|
static void
|
|
link_assign_subroutine_types(struct gl_shader_program *prog)
|
|
{
|
|
unsigned mask = prog->data->linked_stages;
|
|
while (mask) {
|
|
const int i = u_bit_scan(&mask);
|
|
struct gl_program *p = prog->_LinkedShaders[i]->Program;
|
|
|
|
struct set *fn_decl_set =
|
|
_mesa_set_create(NULL, _mesa_hash_string, _mesa_key_string_equal);
|
|
|
|
p->sh.MaxSubroutineFunctionIndex = 0;
|
|
nir_foreach_function(fn, p->nir) {
|
|
/* A function might be decalred multiple times but we should only
|
|
* process it once
|
|
*/
|
|
struct set_entry *entry = _mesa_set_search(fn_decl_set, fn->name);
|
|
if (entry)
|
|
continue;
|
|
|
|
_mesa_set_add(fn_decl_set, fn->name);
|
|
|
|
if (fn->is_subroutine)
|
|
p->sh.NumSubroutineUniformTypes++;
|
|
|
|
if (!fn->num_subroutine_types)
|
|
continue;
|
|
|
|
/* these should have been calculated earlier. */
|
|
assert(fn->subroutine_index != -1);
|
|
if (p->sh.NumSubroutineFunctions + 1 > MAX_SUBROUTINES) {
|
|
linker_error(prog, "Too many subroutine functions declared.\n");
|
|
return;
|
|
}
|
|
p->sh.SubroutineFunctions = reralloc(p, p->sh.SubroutineFunctions,
|
|
struct gl_subroutine_function,
|
|
p->sh.NumSubroutineFunctions + 1);
|
|
p->sh.SubroutineFunctions[p->sh.NumSubroutineFunctions].name.string = ralloc_strdup(p, fn->name);
|
|
resource_name_updated(&p->sh.SubroutineFunctions[p->sh.NumSubroutineFunctions].name);
|
|
p->sh.SubroutineFunctions[p->sh.NumSubroutineFunctions].num_compat_types = fn->num_subroutine_types;
|
|
p->sh.SubroutineFunctions[p->sh.NumSubroutineFunctions].types =
|
|
ralloc_array(p, const struct glsl_type *,
|
|
fn->num_subroutine_types);
|
|
|
|
/* From Section 4.4.4(Subroutine Function Layout Qualifiers) of the
|
|
* GLSL 4.5 spec:
|
|
*
|
|
* "Each subroutine with an index qualifier in the shader must be
|
|
* given a unique index, otherwise a compile or link error will be
|
|
* generated."
|
|
*/
|
|
for (unsigned j = 0; j < p->sh.NumSubroutineFunctions; j++) {
|
|
if (p->sh.SubroutineFunctions[j].index != -1 &&
|
|
p->sh.SubroutineFunctions[j].index == fn->subroutine_index) {
|
|
linker_error(prog, "each subroutine index qualifier in the "
|
|
"shader must be unique\n");
|
|
return;
|
|
}
|
|
}
|
|
p->sh.SubroutineFunctions[p->sh.NumSubroutineFunctions].index =
|
|
fn->subroutine_index;
|
|
|
|
if (fn->subroutine_index > (int)p->sh.MaxSubroutineFunctionIndex)
|
|
p->sh.MaxSubroutineFunctionIndex = fn->subroutine_index;
|
|
|
|
for (int j = 0; j < fn->num_subroutine_types; j++)
|
|
p->sh.SubroutineFunctions[p->sh.NumSubroutineFunctions].types[j] = fn->subroutine_types[j];
|
|
p->sh.NumSubroutineFunctions++;
|
|
}
|
|
|
|
_mesa_set_destroy(fn_decl_set, NULL);
|
|
}
|
|
}
|
|
|
|
static void
|
|
verify_subroutine_associated_funcs(struct gl_shader_program *prog)
|
|
{
|
|
unsigned mask = prog->data->linked_stages;
|
|
while (mask) {
|
|
const int i = u_bit_scan(&mask);
|
|
struct gl_program *p = prog->_LinkedShaders[i]->Program;
|
|
|
|
/* Section 6.1.2 (Subroutines) of the GLSL 4.00 spec says:
|
|
*
|
|
* "A program will fail to compile or link if any shader
|
|
* or stage contains two or more functions with the same
|
|
* name if the name is associated with a subroutine type."
|
|
*/
|
|
for (unsigned j = 0; j < p->sh.NumSubroutineFunctions; j++) {
|
|
unsigned definitions = 0;
|
|
char *name = p->sh.SubroutineFunctions[j].name.string;
|
|
|
|
/* Calculate number of function definitions with the same name */
|
|
nir_foreach_function(fn, p->nir) {
|
|
/* If the function is only declared not implemented continue */
|
|
if (fn->impl != NULL)
|
|
continue;
|
|
|
|
if (strcmp(fn->name, name) == 0) {
|
|
if (++definitions > 1) {
|
|
linker_error(prog, "%s shader contains two or more function "
|
|
"definitions with name `%s', which is "
|
|
"associated with a subroutine type.\n",
|
|
_mesa_shader_stage_to_string(i),
|
|
fn->name);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Validate shader image resources.
|
|
*/
|
|
static void
|
|
check_image_resources(const struct gl_constants *consts,
|
|
const struct gl_extensions *exts,
|
|
struct gl_shader_program *prog)
|
|
{
|
|
unsigned total_image_units = 0;
|
|
unsigned fragment_outputs = 0;
|
|
unsigned total_shader_storage_blocks = 0;
|
|
|
|
if (!exts->ARB_shader_image_load_store)
|
|
return;
|
|
|
|
for (unsigned i = 0; i < MESA_SHADER_STAGES; i++) {
|
|
struct gl_linked_shader *sh = prog->_LinkedShaders[i];
|
|
if (!sh)
|
|
continue;
|
|
|
|
total_image_units += sh->Program->info.num_images;
|
|
total_shader_storage_blocks += sh->Program->info.num_ssbos;
|
|
}
|
|
|
|
if (total_image_units > consts->MaxCombinedImageUniforms)
|
|
linker_error(prog, "Too many combined image uniforms\n");
|
|
|
|
struct gl_linked_shader *frag_sh =
|
|
prog->_LinkedShaders[MESA_SHADER_FRAGMENT];
|
|
if (frag_sh) {
|
|
uint64_t frag_outputs_written = frag_sh->Program->info.outputs_written;
|
|
fragment_outputs = util_bitcount64(frag_outputs_written);
|
|
}
|
|
|
|
if (total_image_units + fragment_outputs + total_shader_storage_blocks >
|
|
consts->MaxCombinedShaderOutputResources)
|
|
linker_error(prog, "Too many combined image uniforms, shader storage "
|
|
" buffers and fragment outputs\n");
|
|
}
|
|
|
|
static bool
|
|
is_sampler_array_accessed_indirectly(nir_deref_instr *deref)
|
|
{
|
|
for (nir_deref_instr *d = deref; d; d = nir_deref_instr_parent(d)) {
|
|
if (d->deref_type != nir_deref_type_array)
|
|
continue;
|
|
|
|
if (nir_src_is_const(d->arr.index))
|
|
continue;
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* This check is done to make sure we allow only constant expression
|
|
* indexing and "constant-index-expression" (indexing with an expression
|
|
* that includes loop induction variable).
|
|
*/
|
|
static bool
|
|
validate_sampler_array_indexing(const struct pipe_screen *screen,
|
|
const struct gl_constants *consts,
|
|
struct gl_shader_program *prog)
|
|
{
|
|
for (unsigned i = 0; i < MESA_SHADER_STAGES; i++) {
|
|
if (prog->_LinkedShaders[i] == NULL)
|
|
continue;
|
|
|
|
bool no_dynamic_indexing =
|
|
screen->nir_options[i]->force_indirect_unrolling_sampler;
|
|
|
|
bool uses_indirect_sampler_array_indexing = false;
|
|
nir_foreach_function_impl(impl, prog->_LinkedShaders[i]->Program->nir) {
|
|
nir_foreach_block(block, impl) {
|
|
nir_foreach_instr(instr, block) {
|
|
/* Check if a sampler array is accessed indirectly */
|
|
if (instr->type == nir_instr_type_tex) {
|
|
nir_tex_instr *tex_instr = nir_instr_as_tex(instr);
|
|
int sampler_idx =
|
|
nir_tex_instr_src_index(tex_instr, nir_tex_src_sampler_deref);
|
|
if (sampler_idx >= 0) {
|
|
nir_deref_instr *deref =
|
|
nir_def_as_deref(tex_instr->src[sampler_idx].src.ssa);
|
|
if (is_sampler_array_accessed_indirectly(deref)) {
|
|
uses_indirect_sampler_array_indexing = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (uses_indirect_sampler_array_indexing)
|
|
break;
|
|
}
|
|
if (uses_indirect_sampler_array_indexing)
|
|
break;
|
|
}
|
|
|
|
if (uses_indirect_sampler_array_indexing) {
|
|
const char *msg = "sampler arrays indexed with non-constant "
|
|
"expressions is forbidden in GLSL %s %u";
|
|
/* Backend has indicated that it has no dynamic indexing support. */
|
|
if (no_dynamic_indexing) {
|
|
linker_error(prog, msg, prog->IsES ? "ES" : "", prog->GLSL_Version);
|
|
return false;
|
|
} else {
|
|
linker_warning(prog, msg, prog->IsES ? "ES" : "",
|
|
prog->GLSL_Version);
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static nir_variable *
|
|
find_frag_builtin(nir_shader *shader, bool is_sysval, unsigned sysval,
|
|
unsigned varying)
|
|
{
|
|
|
|
unsigned location = is_sysval ? sysval : varying;
|
|
nir_variable_mode mode =
|
|
is_sysval ? nir_var_system_value : nir_var_shader_in;
|
|
|
|
return nir_find_variable_with_location(shader, mode, location);
|
|
}
|
|
|
|
/**
|
|
* Verifies the invariance of built-in special variables.
|
|
*/
|
|
static bool
|
|
validate_invariant_builtins(const struct gl_constants *consts,
|
|
struct gl_shader_program *prog,
|
|
const struct gl_linked_shader *vert,
|
|
const struct gl_linked_shader *frag)
|
|
{
|
|
const nir_variable *var_vert;
|
|
const nir_variable *var_frag;
|
|
|
|
if (!vert || !frag)
|
|
return true;
|
|
|
|
/*
|
|
* From OpenGL ES Shading Language 1.0 specification
|
|
* (4.6.4 Invariance and Linkage):
|
|
* "The invariance of varyings that are declared in both the vertex and
|
|
* fragment shaders must match. For the built-in special variables,
|
|
* gl_FragCoord can only be declared invariant if and only if
|
|
* gl_Position is declared invariant. Similarly gl_PointCoord can only
|
|
* be declared invariant if and only if gl_PointSize is declared
|
|
* invariant. It is an error to declare gl_FrontFacing as invariant.
|
|
* The invariance of gl_FrontFacing is the same as the invariance of
|
|
* gl_Position."
|
|
*/
|
|
var_frag = find_frag_builtin(frag->Program->nir,
|
|
consts->GLSLFragCoordIsSysVal,
|
|
SYSTEM_VALUE_FRAG_COORD, VARYING_SLOT_POS);
|
|
if (var_frag && var_frag->data.invariant) {
|
|
var_vert = nir_find_variable_with_location(vert->Program->nir,
|
|
nir_var_shader_out,
|
|
VARYING_SLOT_POS);
|
|
if (var_vert && !var_vert->data.invariant) {
|
|
linker_error(prog,
|
|
"fragment shader built-in `%s' has invariant qualifier, "
|
|
"but vertex shader built-in `%s' lacks invariant qualifier\n",
|
|
var_frag->name, var_vert->name);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
var_frag = find_frag_builtin(frag->Program->nir,
|
|
consts->GLSLPointCoordIsSysVal,
|
|
SYSTEM_VALUE_POINT_COORD, VARYING_SLOT_PNTC);
|
|
if (var_frag && var_frag->data.invariant) {
|
|
var_vert = nir_find_variable_with_location(vert->Program->nir,
|
|
nir_var_shader_out,
|
|
VARYING_SLOT_PSIZ);
|
|
if (var_vert && !var_vert->data.invariant) {
|
|
linker_error(prog,
|
|
"fragment shader built-in `%s' has invariant qualifier, "
|
|
"but vertex shader built-in `%s' lacks invariant qualifier\n",
|
|
var_frag->name, var_vert->name);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
var_frag = find_frag_builtin(frag->Program->nir,
|
|
consts->GLSLFrontFacingIsSysVal,
|
|
SYSTEM_VALUE_FRONT_FACE, VARYING_SLOT_FACE);
|
|
if (var_frag && var_frag->data.invariant) {
|
|
linker_error(prog,
|
|
"fragment shader built-in `%s' can not be declared as invariant\n",
|
|
var_frag->name);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static void
|
|
find_assignments(nir_shader *shader, nir_variable *var1, nir_variable *var2,
|
|
nir_variable *var3, bool *var1_written, bool *var2_written,
|
|
bool *var3_written)
|
|
{
|
|
nir_foreach_function_impl(impl, shader) {
|
|
nir_foreach_block(block, impl) {
|
|
nir_foreach_instr(instr, block) {
|
|
if (instr->type == nir_instr_type_intrinsic) {
|
|
nir_intrinsic_instr *intrin = nir_instr_as_intrinsic(instr);
|
|
if (intrin->intrinsic == nir_intrinsic_store_deref ||
|
|
intrin->intrinsic == nir_intrinsic_copy_deref) {
|
|
nir_deref_instr *deref = nir_src_as_deref(intrin->src[0]);
|
|
nir_variable *var = nir_deref_instr_get_variable(deref);
|
|
if (!var)
|
|
continue;
|
|
|
|
if (var == var1)
|
|
*var1_written = true;
|
|
else if (var == var2)
|
|
*var2_written = true;
|
|
else if (var == var3)
|
|
*var3_written = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Set clip_distance_array_size based and cull_distance_array_size on the given
|
|
* shader.
|
|
*
|
|
* Also check for errors based on incorrect usage of gl_ClipVertex and
|
|
* gl_ClipDistance and gl_CullDistance.
|
|
* Additionally test whether the arrays gl_ClipDistance and gl_CullDistance
|
|
* exceed the maximum size defined by gl_MaxCombinedClipAndCullDistances.
|
|
*/
|
|
static void
|
|
analyze_clip_cull_usage(struct gl_shader_program *prog, nir_shader *shader,
|
|
const struct gl_constants *consts,
|
|
struct shader_info *info)
|
|
{
|
|
if (consts->DoDCEBeforeClipCullAnalysis) {
|
|
/* Remove dead functions to avoid raising an error (eg: dead function
|
|
* writes to gl_ClipVertex, and main() writes to gl_ClipDistance).
|
|
*/
|
|
remove_dead_functions(shader);
|
|
}
|
|
|
|
info->clip_distance_array_size = 0;
|
|
info->cull_distance_array_size = 0;
|
|
|
|
if (prog->GLSL_Version >= (prog->IsES ? 300 : 130)) {
|
|
/* From section 7.1 (Vertex Shader Special Variables) of the
|
|
* GLSL 1.30 spec:
|
|
*
|
|
* "It is an error for a shader to statically write both
|
|
* gl_ClipVertex and gl_ClipDistance."
|
|
*
|
|
* This does not apply to GLSL ES shaders, since GLSL ES defines neither
|
|
* gl_ClipVertex nor gl_ClipDistance. However with
|
|
* GL_EXT_clip_cull_distance, this functionality is exposed in ES 3.0.
|
|
*/
|
|
nir_variable *clip_dist =
|
|
nir_find_variable_with_location(shader,
|
|
nir_var_shader_out,
|
|
VARYING_SLOT_CLIP_DIST0);
|
|
nir_variable *cull_dist =
|
|
nir_find_variable_with_location(shader,
|
|
nir_var_shader_out,
|
|
VARYING_SLOT_CULL_DIST0);
|
|
nir_variable *clip_vert =
|
|
nir_find_variable_with_location(shader,
|
|
nir_var_shader_out,
|
|
VARYING_SLOT_CLIP_VERTEX);
|
|
|
|
bool clip_dist_written = false;
|
|
bool cull_dist_written = false;
|
|
bool clip_vert_written = false;
|
|
find_assignments(shader, clip_dist, cull_dist, clip_vert,
|
|
&clip_dist_written, &cull_dist_written,
|
|
&clip_vert_written);
|
|
|
|
/* From the ARB_cull_distance spec:
|
|
*
|
|
* It is a compile-time or link-time error for the set of shaders forming
|
|
* a program to statically read or write both gl_ClipVertex and either
|
|
* gl_ClipDistance or gl_CullDistance.
|
|
*
|
|
* This does not apply to GLSL ES shaders, since GLSL ES doesn't define
|
|
* gl_ClipVertex.
|
|
*/
|
|
if (!prog->IsES) {
|
|
if (clip_vert_written && clip_dist_written) {
|
|
linker_error(prog, "%s shader writes to both `gl_ClipVertex' "
|
|
"and `gl_ClipDistance'\n",
|
|
_mesa_shader_stage_to_string(info->stage));
|
|
return;
|
|
}
|
|
if (clip_vert_written && cull_dist_written) {
|
|
linker_error(prog, "%s shader writes to both `gl_ClipVertex' "
|
|
"and `gl_CullDistance'\n",
|
|
_mesa_shader_stage_to_string(info->stage));
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (clip_dist_written)
|
|
info->clip_distance_array_size = glsl_get_length(clip_dist->type);
|
|
|
|
if (cull_dist_written)
|
|
info->cull_distance_array_size = glsl_get_length(cull_dist->type);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Verify that a vertex shader executable meets all semantic requirements.
|
|
*
|
|
* Also sets info.clip_distance_array_size and
|
|
* info.cull_distance_array_size as a side effect.
|
|
*
|
|
* \param shader Vertex shader executable to be verified
|
|
*/
|
|
static void
|
|
validate_vertex_shader_executable(struct gl_shader_program *prog,
|
|
nir_shader *shader,
|
|
const struct gl_constants *consts)
|
|
{
|
|
if (shader == NULL)
|
|
return;
|
|
|
|
/* From the GLSL 1.10 spec, page 48:
|
|
*
|
|
* "The variable gl_Position is available only in the vertex
|
|
* language and is intended for writing the homogeneous vertex
|
|
* position. All executions of a well-formed vertex shader
|
|
* executable must write a value into this variable. [...] The
|
|
* variable gl_Position is available only in the vertex
|
|
* language and is intended for writing the homogeneous vertex
|
|
* position. All executions of a well-formed vertex shader
|
|
* executable must write a value into this variable."
|
|
*
|
|
* while in GLSL 1.40 this text is changed to:
|
|
*
|
|
* "The variable gl_Position is available only in the vertex
|
|
* language and is intended for writing the homogeneous vertex
|
|
* position. It can be written at any time during shader
|
|
* execution. It may also be read back by a vertex shader
|
|
* after being written. This value will be used by primitive
|
|
* assembly, clipping, culling, and other fixed functionality
|
|
* operations, if present, that operate on primitives after
|
|
* vertex processing has occurred. Its value is undefined if
|
|
* the vertex shader executable does not write gl_Position."
|
|
*
|
|
* All GLSL ES Versions are similar to GLSL 1.40--failing to write to
|
|
* gl_Position is not an error.
|
|
*/
|
|
if (prog->GLSL_Version < (prog->IsES ? 300 : 140)) {
|
|
nir_variable *gl_position =
|
|
nir_find_variable_with_location(shader,
|
|
nir_var_shader_out,
|
|
VARYING_SLOT_POS);
|
|
|
|
bool gl_position_written = false;
|
|
find_assignments(shader, gl_position, NULL, NULL, &gl_position_written,
|
|
NULL, NULL);
|
|
if (!gl_position_written) {
|
|
if (prog->IsES) {
|
|
linker_warning(prog,
|
|
"vertex shader does not write to `gl_Position'. "
|
|
"Its value is undefined. \n");
|
|
} else {
|
|
linker_error(prog,
|
|
"vertex shader does not write to `gl_Position'. \n");
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
|
|
analyze_clip_cull_usage(prog, shader, consts, &shader->info);
|
|
}
|
|
|
|
static void
|
|
validate_tess_eval_shader_executable(struct gl_shader_program *prog,
|
|
nir_shader *shader,
|
|
const struct gl_constants *consts)
|
|
{
|
|
if (shader == NULL)
|
|
return;
|
|
|
|
analyze_clip_cull_usage(prog, shader, consts, &shader->info);
|
|
}
|
|
|
|
/**
|
|
* Verify that a fragment shader executable meets all semantic requirements
|
|
*
|
|
* \param shader Fragment shader executable to be verified
|
|
*/
|
|
static void
|
|
validate_fragment_shader_executable(struct gl_shader_program *prog,
|
|
nir_shader *shader)
|
|
{
|
|
if (shader == NULL)
|
|
return;
|
|
|
|
nir_variable *gl_frag_color =
|
|
nir_find_variable_with_location(shader,
|
|
nir_var_shader_out,
|
|
FRAG_RESULT_COLOR);
|
|
nir_variable *gl_frag_data =
|
|
nir_find_variable_with_location(shader,
|
|
nir_var_shader_out,
|
|
FRAG_RESULT_DATA0);
|
|
|
|
bool gl_frag_color_written = false;
|
|
bool gl_frag_data_written = false;
|
|
find_assignments(shader, gl_frag_color, gl_frag_data, NULL,
|
|
&gl_frag_color_written, &gl_frag_data_written, NULL);
|
|
|
|
if (gl_frag_color_written && gl_frag_data_written) {
|
|
linker_error(prog, "fragment shader writes to both "
|
|
"`gl_FragColor' and `gl_FragData'\n");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Verify that a geometry shader executable meets all semantic requirements
|
|
*
|
|
* Also sets prog->Geom.VerticesIn, and info.clip_distance_array_sizeand
|
|
* info.cull_distance_array_size as a side effect.
|
|
*
|
|
* \param shader Geometry shader executable to be verified
|
|
*/
|
|
static void
|
|
validate_geometry_shader_executable(struct gl_shader_program *prog,
|
|
nir_shader *shader,
|
|
const struct gl_constants *consts)
|
|
{
|
|
if (shader == NULL)
|
|
return;
|
|
|
|
unsigned num_vertices =
|
|
mesa_vertices_per_prim(shader->info.gs.input_primitive);
|
|
shader->info.gs.vertices_in = num_vertices;
|
|
|
|
analyze_clip_cull_usage(prog, shader, consts, &shader->info);
|
|
}
|
|
|
|
bool
|
|
gl_nir_link_glsl(struct gl_context *ctx, struct gl_shader_program *prog)
|
|
{
|
|
const struct gl_constants *consts = &ctx->Const;
|
|
const struct gl_extensions *exts = &ctx->Extensions;
|
|
gl_api api = ctx->API;
|
|
|
|
if (prog->NumShaders == 0)
|
|
return true;
|
|
|
|
MESA_TRACE_FUNC();
|
|
|
|
void *mem_ctx = ralloc_context(NULL); /* temporary linker context */
|
|
|
|
/* Separate the shaders into groups based on their type.
|
|
*/
|
|
struct gl_shader **shader_list[MESA_SHADER_STAGES];
|
|
unsigned num_shaders[MESA_SHADER_STAGES];
|
|
|
|
for (int i = 0; i < MESA_SHADER_STAGES; i++) {
|
|
shader_list[i] = (struct gl_shader **)
|
|
calloc(prog->NumShaders, sizeof(struct gl_shader *));
|
|
num_shaders[i] = 0;
|
|
}
|
|
|
|
unsigned min_version = UINT_MAX;
|
|
unsigned max_version = 0;
|
|
for (unsigned i = 0; i < prog->NumShaders; i++) {
|
|
min_version = MIN2(min_version, prog->Shaders[i]->Version);
|
|
max_version = MAX2(max_version, prog->Shaders[i]->Version);
|
|
|
|
if (!consts->AllowGLSLRelaxedES &&
|
|
prog->Shaders[i]->IsES != prog->Shaders[0]->IsES) {
|
|
linker_error(prog, "all shaders must use same shading "
|
|
"language version\n");
|
|
goto done;
|
|
}
|
|
|
|
mesa_shader_stage shader_type = prog->Shaders[i]->Stage;
|
|
shader_list[shader_type][num_shaders[shader_type]] = prog->Shaders[i];
|
|
num_shaders[shader_type]++;
|
|
}
|
|
|
|
/* In desktop GLSL, different shader versions may be linked together. In
|
|
* GLSL ES, all shader versions must be the same.
|
|
*/
|
|
if (!consts->AllowGLSLRelaxedES && prog->Shaders[0]->IsES &&
|
|
min_version != max_version) {
|
|
linker_error(prog, "all shaders must use same shading "
|
|
"language version\n");
|
|
goto done;
|
|
}
|
|
|
|
prog->GLSL_Version = max_version;
|
|
prog->IsES = prog->Shaders[0]->IsES;
|
|
|
|
/* Some shaders have to be linked with some other shaders present.
|
|
*/
|
|
if (!prog->SeparateShader) {
|
|
if (num_shaders[MESA_SHADER_GEOMETRY] > 0 &&
|
|
num_shaders[MESA_SHADER_VERTEX] == 0) {
|
|
linker_error(prog, "Geometry shader must be linked with "
|
|
"vertex shader\n");
|
|
goto done;
|
|
}
|
|
if (num_shaders[MESA_SHADER_TESS_EVAL] > 0 &&
|
|
num_shaders[MESA_SHADER_VERTEX] == 0) {
|
|
linker_error(prog, "Tessellation evaluation shader must be linked "
|
|
"with vertex shader\n");
|
|
goto done;
|
|
}
|
|
if (num_shaders[MESA_SHADER_TESS_CTRL] > 0 &&
|
|
num_shaders[MESA_SHADER_VERTEX] == 0) {
|
|
linker_error(prog, "Tessellation control shader must be linked with "
|
|
"vertex shader\n");
|
|
goto done;
|
|
}
|
|
|
|
/* Section 7.3 of the OpenGL ES 3.2 specification says:
|
|
*
|
|
* "Linking can fail for [...] any of the following reasons:
|
|
*
|
|
* * program contains an object to form a tessellation control
|
|
* shader [...] and [...] the program is not separable and
|
|
* contains no object to form a tessellation evaluation shader"
|
|
*
|
|
* The OpenGL spec is contradictory. It allows linking without a tess
|
|
* eval shader, but that can only be used with transform feedback and
|
|
* rasterization disabled. However, transform feedback isn't allowed
|
|
* with GL_PATCHES, so it can't be used.
|
|
*
|
|
* More investigation showed that the idea of transform feedback after
|
|
* a tess control shader was dropped, because some hw vendors couldn't
|
|
* support tessellation without a tess eval shader, but the linker
|
|
* section wasn't updated to reflect that.
|
|
*
|
|
* All specifications (ARB_tessellation_shader, GL 4.0-4.5) have this
|
|
* spec bug.
|
|
*
|
|
* Do what's reasonable and always require a tess eval shader if a tess
|
|
* control shader is present.
|
|
*/
|
|
if (num_shaders[MESA_SHADER_TESS_CTRL] > 0 &&
|
|
num_shaders[MESA_SHADER_TESS_EVAL] == 0) {
|
|
linker_error(prog, "Tessellation control shader must be linked with "
|
|
"tessellation evaluation shader\n");
|
|
goto done;
|
|
}
|
|
|
|
if (prog->IsES) {
|
|
if (num_shaders[MESA_SHADER_TESS_EVAL] > 0 &&
|
|
num_shaders[MESA_SHADER_TESS_CTRL] == 0) {
|
|
linker_error(prog, "GLSL ES requires non-separable programs "
|
|
"containing a tessellation evaluation shader to also "
|
|
"be linked with a tessellation control shader\n");
|
|
goto done;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Compute shaders have additional restrictions. */
|
|
if (num_shaders[MESA_SHADER_COMPUTE] > 0 &&
|
|
num_shaders[MESA_SHADER_COMPUTE] != prog->NumShaders) {
|
|
linker_error(prog, "Compute shaders may not be linked with any other "
|
|
"type of shader\n");
|
|
}
|
|
|
|
/* Link all shaders for a particular stage and validate the result.
|
|
*/
|
|
for (int stage = 0; stage < MESA_SHADER_STAGES; stage++) {
|
|
if (num_shaders[stage] > 0) {
|
|
struct gl_linked_shader *const sh =
|
|
link_intrastage_shaders(mem_ctx, ctx, prog, shader_list[stage],
|
|
num_shaders[stage]);
|
|
|
|
if (!prog->data->LinkStatus) {
|
|
if (sh)
|
|
_mesa_delete_linked_shader(ctx, sh);
|
|
goto done;
|
|
}
|
|
|
|
prog->_LinkedShaders[stage] = sh;
|
|
prog->data->linked_stages |= 1 << stage;
|
|
}
|
|
}
|
|
|
|
/* Link all shaders for a particular stage and validate the result.
|
|
*/
|
|
for (int stage = 0; stage < MESA_SHADER_STAGES; stage++) {
|
|
struct gl_linked_shader *sh = prog->_LinkedShaders[stage];
|
|
if (sh) {
|
|
nir_shader *shader = sh->Program->nir;
|
|
|
|
/* Parameters will be filled during NIR linking. */
|
|
sh->Program->Parameters = _mesa_new_parameter_list();
|
|
sh->Program->shader_program = prog;
|
|
shader->info.separate_shader = prog->SeparateShader;
|
|
|
|
switch (stage) {
|
|
case MESA_SHADER_VERTEX:
|
|
validate_vertex_shader_executable(prog, shader, consts);
|
|
break;
|
|
case MESA_SHADER_TESS_CTRL:
|
|
/* nothing to be done */
|
|
break;
|
|
case MESA_SHADER_TESS_EVAL:
|
|
validate_tess_eval_shader_executable(prog, shader, consts);
|
|
break;
|
|
case MESA_SHADER_GEOMETRY:
|
|
validate_geometry_shader_executable(prog, shader, consts);
|
|
break;
|
|
case MESA_SHADER_FRAGMENT:
|
|
validate_fragment_shader_executable(prog, shader);
|
|
break;
|
|
}
|
|
if (!prog->data->LinkStatus) {
|
|
_mesa_delete_linked_shader(ctx, sh);
|
|
|
|
prog->_LinkedShaders[stage] = NULL;
|
|
prog->data->linked_stages ^= 1 << stage;
|
|
|
|
goto done;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Here begins the inter-stage linking phase. Some initial validation is
|
|
* performed, then locations are assigned for uniforms, attributes, and
|
|
* varyings.
|
|
*/
|
|
cross_validate_uniforms(consts, prog);
|
|
if (!prog->data->LinkStatus)
|
|
goto done;
|
|
|
|
check_explicit_uniform_locations(exts, prog);
|
|
|
|
link_assign_subroutine_types(prog);
|
|
verify_subroutine_associated_funcs(prog);
|
|
if (!prog->data->LinkStatus)
|
|
goto done;
|
|
|
|
for (unsigned i = 0; i < MESA_SHADER_STAGES; i++) {
|
|
if (prog->_LinkedShaders[i] == NULL)
|
|
continue;
|
|
|
|
gl_nir_detect_recursion_linked(prog,
|
|
prog->_LinkedShaders[i]->Program->nir);
|
|
if (!prog->data->LinkStatus)
|
|
goto done;
|
|
|
|
gl_nir_inline_functions(prog->_LinkedShaders[i]->Program->nir);
|
|
}
|
|
|
|
resize_tes_inputs(consts, prog);
|
|
set_geom_shader_input_array_size(prog);
|
|
|
|
/* Validate the inputs of each stage with the output of the preceding
|
|
* stage.
|
|
*/
|
|
unsigned prev = MESA_SHADER_STAGES;
|
|
for (unsigned i = 0; i <= MESA_SHADER_FRAGMENT; i++) {
|
|
if (prog->_LinkedShaders[i] == NULL)
|
|
continue;
|
|
|
|
if (prev == MESA_SHADER_STAGES) {
|
|
prev = i;
|
|
continue;
|
|
}
|
|
|
|
gl_nir_validate_interstage_inout_blocks(prog, prog->_LinkedShaders[prev],
|
|
prog->_LinkedShaders[i]);
|
|
if (!prog->data->LinkStatus)
|
|
goto done;
|
|
|
|
prev = i;
|
|
}
|
|
|
|
/* Cross-validate uniform blocks between shader stages */
|
|
gl_nir_validate_interstage_uniform_blocks(prog, prog->_LinkedShaders);
|
|
if (!prog->data->LinkStatus)
|
|
goto done;
|
|
|
|
if (prog->IsES && prog->GLSL_Version == 100)
|
|
if (!validate_invariant_builtins(consts, prog,
|
|
prog->_LinkedShaders[MESA_SHADER_VERTEX],
|
|
prog->_LinkedShaders[MESA_SHADER_FRAGMENT]))
|
|
goto done;
|
|
|
|
/* Check and validate stream emissions in geometry shaders */
|
|
validate_geometry_shader_emissions(consts, prog);
|
|
|
|
prog->last_vert_prog = NULL;
|
|
for (int i = MESA_SHADER_GEOMETRY; i >= MESA_SHADER_VERTEX; i--) {
|
|
if (prog->_LinkedShaders[i] == NULL)
|
|
continue;
|
|
|
|
prog->last_vert_prog = prog->_LinkedShaders[i]->Program;
|
|
break;
|
|
}
|
|
|
|
unsigned first = MESA_SHADER_STAGES;
|
|
unsigned last = 0;
|
|
|
|
/* Determine first and last stage. */
|
|
for (unsigned i = 0; i < MESA_SHADER_STAGES; i++) {
|
|
if (!prog->_LinkedShaders[i])
|
|
continue;
|
|
if (first == MESA_SHADER_STAGES)
|
|
first = i;
|
|
last = i;
|
|
}
|
|
|
|
/* Implement the GLSL 1.30+ rule for discard vs infinite loops.
|
|
* This rule also applies to GLSL ES 3.00.
|
|
*/
|
|
if (prog->GLSL_Version >= (prog->IsES ? 300 : 130)) {
|
|
struct gl_linked_shader *sh = prog->_LinkedShaders[MESA_SHADER_FRAGMENT];
|
|
if (sh)
|
|
gl_nir_lower_discard_flow(sh->Program->nir);
|
|
}
|
|
|
|
gl_nir_lower_named_interface_blocks(prog);
|
|
|
|
/* Validate the inputs of each stage with the output of the preceding
|
|
* stage.
|
|
*/
|
|
prev = first;
|
|
for (unsigned i = prev + 1; i <= MESA_SHADER_FRAGMENT; i++) {
|
|
if (prog->_LinkedShaders[i] == NULL)
|
|
continue;
|
|
|
|
gl_nir_cross_validate_outputs_to_inputs(consts, prog,
|
|
prog->_LinkedShaders[prev],
|
|
prog->_LinkedShaders[i]);
|
|
if (!prog->data->LinkStatus)
|
|
goto done;
|
|
|
|
prev = i;
|
|
}
|
|
|
|
/* The cross validation of outputs/inputs above validates interstage
|
|
* explicit locations. We need to do this also for the inputs in the first
|
|
* stage and outputs of the last stage included in the program, since there
|
|
* is no cross validation for these.
|
|
*/
|
|
if (!gl_nir_validate_first_and_last_interface_explicit_locations(consts, prog,
|
|
(mesa_shader_stage)first,
|
|
(mesa_shader_stage)last))
|
|
goto done;
|
|
|
|
if (prog->SeparateShader)
|
|
disable_varying_optimizations_for_sso(prog);
|
|
|
|
struct gl_linked_shader *linked_shader[MESA_SHADER_STAGES];
|
|
unsigned num_linked_shaders = 0;
|
|
|
|
for (unsigned i = 0; i < MESA_SHADER_STAGES; i++) {
|
|
if (prog->_LinkedShaders[i]) {
|
|
linked_shader[num_linked_shaders++] = prog->_LinkedShaders[i];
|
|
|
|
/* Section 13.46 (Vertex Attribute Aliasing) of the OpenGL ES 3.2
|
|
* specification says:
|
|
*
|
|
* "In general, the behavior of GLSL ES should not depend on
|
|
* compiler optimizations which might be implementation-dependent.
|
|
* Name matching rules in most languages, including C++ from which
|
|
* GLSL ES is derived, are based on declarations rather than use.
|
|
*
|
|
* RESOLUTION: The existence of aliasing is determined by
|
|
* declarations present after preprocessing."
|
|
*
|
|
* Because of this rule, we don't remove dead attributes before
|
|
* attribute assignment for vertex shader inputs here.
|
|
*/
|
|
if (!(prog->IsES && prog->GLSL_Version >= 300 && i == MESA_SHADER_VERTEX))
|
|
remove_dead_varyings_pre_linking(prog->_LinkedShaders[i]->Program->nir);
|
|
}
|
|
}
|
|
|
|
if (!gl_assign_attribute_or_color_locations(consts, prog))
|
|
goto done;
|
|
|
|
if (!prelink_lowering(ctx->screen, consts, exts, prog, linked_shader,
|
|
num_linked_shaders))
|
|
goto done;
|
|
|
|
if (!gl_nir_link_varyings(ctx->screen, consts, exts, api, prog))
|
|
goto done;
|
|
|
|
/* Validation for special cases where we allow sampler array indexing
|
|
* with loop induction variable. This check emits a warning or error
|
|
* depending if backend can handle dynamic indexing.
|
|
*/
|
|
if ((!prog->IsES && prog->GLSL_Version < 130) ||
|
|
(prog->IsES && prog->GLSL_Version < 300)) {
|
|
if (!validate_sampler_array_indexing(ctx->screen, consts, prog))
|
|
goto done;
|
|
}
|
|
|
|
if (prog->data->LinkStatus == LINKING_FAILURE)
|
|
goto done;
|
|
|
|
/* Tidy up any left overs from the linking process for single shaders.
|
|
* For example varying arrays that get packed may have dead elements that
|
|
* can be now be eliminated now that array access has been lowered.
|
|
*/
|
|
if (num_linked_shaders == 1)
|
|
gl_nir_opts(linked_shader[0]->Program->nir);
|
|
|
|
for (unsigned i = 0; i < MESA_SHADER_STAGES; i++) {
|
|
struct gl_linked_shader *shader = prog->_LinkedShaders[i];
|
|
if (shader) {
|
|
if (consts->GLSLLowerConstArrays) {
|
|
nir_lower_const_arrays_to_uniforms(shader->Program->nir,
|
|
consts->Program[i].MaxUniformComponents);
|
|
}
|
|
|
|
const nir_remove_dead_variables_options opts = {
|
|
.can_remove_var = can_remove_var,
|
|
};
|
|
nir_remove_dead_variables(shader->Program->nir,
|
|
nir_var_uniform | nir_var_image |
|
|
nir_var_mem_ubo | nir_var_mem_ssbo |
|
|
nir_var_system_value,
|
|
&opts);
|
|
|
|
if (shader->Program->info.stage == MESA_SHADER_FRAGMENT) {
|
|
nir_shader *nir = shader->Program->nir;
|
|
nir_foreach_variable_in_shader(var, nir) {
|
|
if (var->data.mode == nir_var_system_value &&
|
|
(var->data.location == SYSTEM_VALUE_SAMPLE_ID ||
|
|
var->data.location == SYSTEM_VALUE_SAMPLE_POS))
|
|
nir->info.fs.uses_sample_shading = true;
|
|
|
|
if (var->data.mode == nir_var_shader_in && var->data.sample)
|
|
nir->info.fs.uses_sample_shading = true;
|
|
|
|
if (var->data.mode == nir_var_shader_out &&
|
|
var->data.fb_fetch_output)
|
|
nir->info.fs.uses_sample_shading = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!gl_nir_link_uniform_blocks(consts, prog))
|
|
goto done;
|
|
|
|
if (!gl_nir_link_uniforms(consts, prog, true))
|
|
goto done;
|
|
|
|
link_util_calculate_subroutine_compat(prog);
|
|
link_util_check_uniform_resources(consts, prog);
|
|
link_util_check_subroutine_resources(prog);
|
|
check_image_resources(consts, exts, prog);
|
|
gl_nir_link_assign_atomic_counter_resources(consts, prog);
|
|
gl_nir_link_check_atomic_counter_resources(consts, prog);
|
|
|
|
/* OpenGL ES < 3.1 requires that a vertex shader and a fragment shader both
|
|
* be present in a linked program. GL_ARB_ES2_compatibility doesn't say
|
|
* anything about shader linking when one of the shaders (vertex or
|
|
* fragment shader) is absent. So, the extension shouldn't change the
|
|
* behavior specified in GLSL specification.
|
|
*
|
|
* From OpenGL ES 3.1 specification (7.3 Program Objects):
|
|
* "Linking can fail for a variety of reasons as specified in the
|
|
* OpenGL ES Shading Language Specification, as well as any of the
|
|
* following reasons:
|
|
*
|
|
* ...
|
|
*
|
|
* * program contains objects to form either a vertex shader or
|
|
* fragment shader, and program is not separable, and does not
|
|
* contain objects to form both a vertex shader and fragment
|
|
* shader."
|
|
*
|
|
* However, the only scenario in 3.1+ where we don't require them both is
|
|
* when we have a compute shader. For example:
|
|
*
|
|
* - No shaders is a link error.
|
|
* - Geom or Tess without a Vertex shader is a link error which means we
|
|
* always require a Vertex shader and hence a Fragment shader.
|
|
* - Finally a Compute shader linked with any other stage is a link error.
|
|
*/
|
|
if (!prog->SeparateShader && _mesa_is_api_gles2(api) &&
|
|
!prog->_LinkedShaders[MESA_SHADER_COMPUTE]) {
|
|
if (prog->_LinkedShaders[MESA_SHADER_VERTEX] == NULL) {
|
|
linker_error(prog, "program lacks a vertex shader\n");
|
|
} else if (prog->_LinkedShaders[MESA_SHADER_FRAGMENT] == NULL) {
|
|
linker_error(prog, "program lacks a fragment shader\n");
|
|
}
|
|
}
|
|
|
|
done:
|
|
for (unsigned i = 0; i < MESA_SHADER_STAGES; i++) {
|
|
free(shader_list[i]);
|
|
}
|
|
|
|
ralloc_free(mem_ctx);
|
|
|
|
if (prog->data->LinkStatus == LINKING_FAILURE)
|
|
return false;
|
|
|
|
return true;
|
|
}
|