diff options
Diffstat (limited to 'mesalib/src/glsl')
24 files changed, 581 insertions, 78 deletions
diff --git a/mesalib/src/glsl/ast.h b/mesalib/src/glsl/ast.h index fc80e780e..83dfafd75 100644 --- a/mesalib/src/glsl/ast.h +++ b/mesalib/src/glsl/ast.h @@ -513,6 +513,8 @@ struct ast_type_qualifier { /** \name Layout qualifiers for GL_ARB_gpu_shader5 */ /** \{ */ unsigned invocations:1; + unsigned stream:1; /**< Has stream value assigned */ + unsigned explicit_stream:1; /**< stream value assigned explicitly by shader code */ /** \} */ } /** \brief Set of flags, accessed by name. */ @@ -546,6 +548,9 @@ struct ast_type_qualifier { /** Maximum output vertices in GLSL 1.50 geometry shaders. */ int max_vertices; + /** Stream in GLSL 1.50 geometry shaders. */ + unsigned stream; + /** Input or output primitive type in GLSL 1.50 geometry shaders */ GLenum prim_type; diff --git a/mesalib/src/glsl/ast_to_hir.cpp b/mesalib/src/glsl/ast_to_hir.cpp index 7ba04a808..042ef243b 100644 --- a/mesalib/src/glsl/ast_to_hir.cpp +++ b/mesalib/src/glsl/ast_to_hir.cpp @@ -2461,6 +2461,11 @@ apply_type_qualifier_to_variable(const struct ast_type_qualifier *qual, if (qual->flags.q.sample) var->data.sample = 1; + if (state->stage == MESA_SHADER_GEOMETRY && + qual->flags.q.out && qual->flags.q.stream) { + var->data.stream = qual->stream; + } + if (qual->flags.q.attribute && state->stage != MESA_SHADER_VERTEX) { var->type = glsl_type::error_type; _mesa_glsl_error(loc, state, @@ -5100,6 +5105,9 @@ ast_process_structure_or_interface_block(exec_list *instructions, fields[i].centroid = qual->flags.q.centroid ? 1 : 0; fields[i].sample = qual->flags.q.sample ? 1 : 0; + /* Only save explicitly defined streams in block's field */ + fields[i].stream = qual->flags.q.explicit_stream ? qual->stream : -1; + if (qual->flags.q.row_major || qual->flags.q.column_major) { if (!qual->flags.q.uniform) { _mesa_glsl_error(&loc, state, @@ -5548,6 +5556,17 @@ ast_interface_block::hir(exec_list *instructions, var->data.sample = fields[i].sample; var->init_interface_type(block_type); + if (fields[i].stream != -1 && + ((unsigned)fields[i].stream) != this->layout.stream) { + _mesa_glsl_error(&loc, state, + "stream layout qualifier on " + "interface block member `%s' does not match " + "the interface block (%d vs %d)", + var->name, fields[i].stream, this->layout.stream); + } + + var->data.stream = this->layout.stream; + if (redeclaring_per_vertex) { ir_variable *earlier = get_variable_being_redeclared(var, loc, state, diff --git a/mesalib/src/glsl/ast_type.cpp b/mesalib/src/glsl/ast_type.cpp index 77053d5b1..017f23d0e 100644 --- a/mesalib/src/glsl/ast_type.cpp +++ b/mesalib/src/glsl/ast_type.cpp @@ -125,9 +125,13 @@ ast_type_qualifier::merge_qualifier(YYLTYPE *loc, /* Uniform block layout qualifiers get to overwrite each * other (rightmost having priority), while all other * qualifiers currently don't allow duplicates. + * + * Geometry shaders can have several layout qualifiers + * assigning different stream values. */ - if ((this->flags.i & q.flags.i & ~(ubo_mat_mask.flags.i | + if ((state->stage != MESA_SHADER_GEOMETRY) && + (this->flags.i & q.flags.i & ~(ubo_mat_mask.flags.i | ubo_layout_mask.flags.i | ubo_binding_mask.flags.i)) != 0) { _mesa_glsl_error(loc, state, @@ -154,6 +158,39 @@ ast_type_qualifier::merge_qualifier(YYLTYPE *loc, this->max_vertices = q.max_vertices; } + if (state->stage == MESA_SHADER_GEOMETRY && + state->has_explicit_attrib_stream()) { + if (q.flags.q.stream && q.stream >= state->ctx->Const.MaxVertexStreams) { + _mesa_glsl_error(loc, state, + "`stream' value is larger than MAX_VERTEX_STREAMS - 1 " + "(%d > %d)", + q.stream, state->ctx->Const.MaxVertexStreams - 1); + } + if (this->flags.q.explicit_stream && + this->stream >= state->ctx->Const.MaxVertexStreams) { + _mesa_glsl_error(loc, state, + "`stream' value is larger than MAX_VERTEX_STREAMS - 1 " + "(%d > %d)", + this->stream, state->ctx->Const.MaxVertexStreams - 1); + } + + if (!this->flags.q.explicit_stream) { + if (q.flags.q.stream) { + this->flags.q.stream = 1; + this->stream = q.stream; + } else if (!this->flags.q.stream && this->flags.q.out) { + /* Assign default global stream value */ + this->flags.q.stream = 1; + this->stream = state->out_qualifier->stream; + } + } else { + if (q.flags.q.explicit_stream) { + _mesa_glsl_error(loc, state, + "duplicate layout `stream' qualifier"); + } + } + } + if ((q.flags.i & ubo_mat_mask.flags.i) != 0) this->flags.i &= ~ubo_mat_mask.flags.i; if ((q.flags.i & ubo_layout_mask.flags.i) != 0) diff --git a/mesalib/src/glsl/builtin_functions.cpp b/mesalib/src/glsl/builtin_functions.cpp index 7e225b0d5..ee92384d6 100755 --- a/mesalib/src/glsl/builtin_functions.cpp +++ b/mesalib/src/glsl/builtin_functions.cpp @@ -359,6 +359,12 @@ shader_image_load_store(const _mesa_glsl_parse_state *state) state->ARB_shader_image_load_store_enable); } +static bool +gs_streams(const _mesa_glsl_parse_state *state) +{ + return gpu_shader5(state) && gs_only(state); +} + /** @} */ /******************************************************************************/ @@ -594,6 +600,10 @@ private: B0(EmitVertex) B0(EndPrimitive) + ir_function_signature *_EmitStreamVertex(builtin_available_predicate avail, + const glsl_type *stream_type); + ir_function_signature *_EndStreamPrimitive(builtin_available_predicate avail, + const glsl_type *stream_type); B2(textureQueryLod); B1(textureQueryLevels); @@ -1708,6 +1718,14 @@ builtin_builder::create_builtins() add_function("EmitVertex", _EmitVertex(), NULL); add_function("EndPrimitive", _EndPrimitive(), NULL); + add_function("EmitStreamVertex", + _EmitStreamVertex(gs_streams, glsl_type::uint_type), + _EmitStreamVertex(gs_streams, glsl_type::int_type), + NULL); + add_function("EndStreamPrimitive", + _EndStreamPrimitive(gs_streams, glsl_type::uint_type), + _EndStreamPrimitive(gs_streams, glsl_type::int_type), + NULL); add_function("textureQueryLOD", _textureQueryLod(glsl_type::sampler1D_type, glsl_type::float_type), @@ -3872,7 +3890,28 @@ builtin_builder::_EmitVertex() { MAKE_SIG(glsl_type::void_type, gs_only, 0); - body.emit(new(mem_ctx) ir_emit_vertex()); + ir_rvalue *stream = new(mem_ctx) ir_constant(0, 1); + body.emit(new(mem_ctx) ir_emit_vertex(stream)); + + return sig; +} + +ir_function_signature * +builtin_builder::_EmitStreamVertex(builtin_available_predicate avail, + const glsl_type *stream_type) +{ + /* Section 8.12 (Geometry Shader Functions) of the GLSL 4.0 spec says: + * + * "Emit the current values of output variables to the current output + * primitive on stream stream. The argument to stream must be a constant + * integral expression." + */ + ir_variable *stream = + new(mem_ctx) ir_variable(stream_type, "stream", ir_var_const_in); + + MAKE_SIG(glsl_type::void_type, avail, 1, stream); + + body.emit(new(mem_ctx) ir_emit_vertex(var_ref(stream))); return sig; } @@ -3882,7 +3921,28 @@ builtin_builder::_EndPrimitive() { MAKE_SIG(glsl_type::void_type, gs_only, 0); - body.emit(new(mem_ctx) ir_end_primitive()); + ir_rvalue *stream = new(mem_ctx) ir_constant(0, 1); + body.emit(new(mem_ctx) ir_end_primitive(stream)); + + return sig; +} + +ir_function_signature * +builtin_builder::_EndStreamPrimitive(builtin_available_predicate avail, + const glsl_type *stream_type) +{ + /* Section 8.12 (Geometry Shader Functions) of the GLSL 4.0 spec says: + * + * "Completes the current output primitive on stream stream and starts + * a new one. The argument to stream must be a constant integral + * expression." + */ + ir_variable *stream = + new(mem_ctx) ir_variable(stream_type, "stream", ir_var_const_in); + + MAKE_SIG(glsl_type::void_type, avail, 1, stream); + + body.emit(new(mem_ctx) ir_end_primitive(var_ref(stream))); return sig; } diff --git a/mesalib/src/glsl/builtin_variables.cpp b/mesalib/src/glsl/builtin_variables.cpp index b9c69d23c..9f9571619 100644 --- a/mesalib/src/glsl/builtin_variables.cpp +++ b/mesalib/src/glsl/builtin_variables.cpp @@ -160,14 +160,6 @@ static const struct gl_builtin_uniform_element gl_NormalScale_elements[] = { {NULL, {STATE_NORMAL_SCALE}, SWIZZLE_XXXX}, }; -static const struct gl_builtin_uniform_element gl_BumpRotMatrix0MESA_elements[] = { - {NULL, {STATE_INTERNAL, STATE_ROT_MATRIX_0}, SWIZZLE_XYZW}, -}; - -static const struct gl_builtin_uniform_element gl_BumpRotMatrix1MESA_elements[] = { - {NULL, {STATE_INTERNAL, STATE_ROT_MATRIX_1}, SWIZZLE_XYZW}, -}; - static const struct gl_builtin_uniform_element gl_FogParamsOptimizedMESA_elements[] = { {NULL, {STATE_INTERNAL, STATE_FOG_PARAMS_OPTIMIZED}, SWIZZLE_XYZW}, }; @@ -284,8 +276,6 @@ static const struct gl_builtin_uniform_desc _mesa_builtin_uniform_desc[] = { STATEVAR(gl_NormalMatrix), STATEVAR(gl_NormalScale), - STATEVAR(gl_BumpRotMatrix0MESA), - STATEVAR(gl_BumpRotMatrix1MESA), STATEVAR(gl_FogParamsOptimizedMESA), STATEVAR(gl_CurrentAttribVertMESA), STATEVAR(gl_CurrentAttribFragMESA), @@ -761,8 +751,6 @@ builtin_variable_generator::generate_uniforms() add_uniform(mat4_t, "gl_ModelViewProjectionMatrixInverseTranspose"); add_uniform(float_t, "gl_NormalScale"); add_uniform(type("gl_LightModelParameters"), "gl_LightModel"); - add_uniform(vec2_t, "gl_BumpRotMatrix0MESA"); - add_uniform(vec2_t, "gl_BumpRotMatrix1MESA"); add_uniform(vec4_t, "gl_FogParamsOptimizedMESA"); const glsl_type *const mat4_array_type = diff --git a/mesalib/src/glsl/glsl_parser.yy b/mesalib/src/glsl/glsl_parser.yy index 2b2de3047..980acc8ae 100644 --- a/mesalib/src/glsl/glsl_parser.yy +++ b/mesalib/src/glsl/glsl_parser.yy @@ -1396,6 +1396,22 @@ layout_qualifier_id: } } + if (state->stage == MESA_SHADER_GEOMETRY) { + if (match_layout_qualifier("stream", $1, state) == 0 && + state->check_explicit_attrib_stream_allowed(& @3)) { + $$.flags.q.stream = 1; + + if ($3 < 0) { + _mesa_glsl_error(& @3, state, + "invalid stream %d specified", $3); + YYERROR; + } else { + $$.flags.q.explicit_stream = 1; + $$.stream = $3; + } + } + } + static const char * const local_size_qualifiers[3] = { "local_size_x", "local_size_y", @@ -1694,6 +1710,20 @@ storage_qualifier: { memset(& $$, 0, sizeof($$)); $$.flags.q.out = 1; + + if (state->stage == MESA_SHADER_GEOMETRY && + state->has_explicit_attrib_stream()) { + /* Section 4.3.8.2 (Output Layout Qualifiers) of the GLSL 4.00 + * spec says: + * + * "If the block or variable is declared with the stream + * identifier, it is associated with the specified stream; + * otherwise, it is associated with the current default stream." + */ + $$.flags.q.stream = 1; + $$.flags.q.explicit_stream = 0; + $$.stream = state->out_qualifier->stream; + } } | UNIFORM { @@ -2362,6 +2392,18 @@ interface_block: if (!block->layout.merge_qualifier(& @1, state, $1)) { YYERROR; } + + foreach_list_typed (ast_declarator_list, member, link, &block->declarations) { + ast_type_qualifier& qualifier = member->type->qualifier; + if (qualifier.flags.q.stream && qualifier.stream != block->layout.stream) { + _mesa_glsl_error(& @1, state, + "stream layout qualifier on " + "interface block member does not match " + "the interface block (%d vs %d)", + qualifier.stream, block->layout.stream); + YYERROR; + } + } $$ = block; } ; @@ -2435,6 +2477,14 @@ basic_interface_block: block->layout.flags.i |= block_interface_qualifier; + if (state->stage == MESA_SHADER_GEOMETRY && + state->has_explicit_attrib_stream()) { + /* Assign global layout's stream value. */ + block->layout.flags.q.stream = 1; + block->layout.flags.q.explicit_stream = 0; + block->layout.stream = state->out_qualifier->stream; + } + foreach_list_typed (ast_declarator_list, member, link, &block->declarations) { ast_type_qualifier& qualifier = member->type->qualifier; if ((qualifier.flags.i & interface_type_mask) == 0) { @@ -2577,6 +2627,9 @@ layout_defaults: } if (!state->out_qualifier->merge_qualifier(& @1, state, $1)) YYERROR; + + /* Allow future assigments of global out's stream id value */ + state->out_qualifier->flags.q.explicit_stream = 0; } $$ = NULL; } diff --git a/mesalib/src/glsl/glsl_parser_extras.h b/mesalib/src/glsl/glsl_parser_extras.h index 2a5aea477..fd1391c2a 100644 --- a/mesalib/src/glsl/glsl_parser_extras.h +++ b/mesalib/src/glsl/glsl_parser_extras.h @@ -123,6 +123,19 @@ struct _mesa_glsl_parse_state { return check_version(130, 300, locp, "bit-wise operations are forbidden"); } + bool check_explicit_attrib_stream_allowed(YYLTYPE *locp) + { + if (!this->has_explicit_attrib_stream()) { + const char *const requirement = "GL_ARB_gpu_shader5 extension or GLSL 400"; + + _mesa_glsl_error(locp, this, "explicit stream requires %s", + requirement); + return false; + } + + return true; + } + bool check_explicit_attrib_location_allowed(YYLTYPE *locp, const ir_variable *var) { @@ -170,6 +183,11 @@ struct _mesa_glsl_parse_state { return true; } + bool has_explicit_attrib_stream() const + { + return ARB_gpu_shader5_enable || is_version(400, 0); + } + bool has_explicit_attrib_location() const { return ARB_explicit_attrib_location_enable || is_version(330, 300); diff --git a/mesalib/src/glsl/glsl_types.h b/mesalib/src/glsl/glsl_types.h index f6d4a02ab..0b63d4850 100644 --- a/mesalib/src/glsl/glsl_types.h +++ b/mesalib/src/glsl/glsl_types.h @@ -671,6 +671,12 @@ struct glsl_struct_field { * in ir_variable::sample). 0 otherwise. */ unsigned sample:1; + + /** + * For interface blocks, it has a value if this variable uses multiple vertex + * streams (as in ir_variable::stream). -1 otherwise. + */ + int stream; }; static inline unsigned int diff --git a/mesalib/src/glsl/ir.h b/mesalib/src/glsl/ir.h index b4e52d3d0..d5239d4de 100644 --- a/mesalib/src/glsl/ir.h +++ b/mesalib/src/glsl/ir.h @@ -706,6 +706,11 @@ public: int location; /** + * Vertex stream output identifier. + */ + unsigned stream; + + /** * output index for dual source blending. */ int index; @@ -2154,9 +2159,11 @@ private: */ class ir_emit_vertex : public ir_instruction { public: - ir_emit_vertex() - : ir_instruction(ir_type_emit_vertex) + ir_emit_vertex(ir_rvalue *stream) + : ir_instruction(ir_type_emit_vertex), + stream(stream) { + assert(stream); } virtual void accept(ir_visitor *v) @@ -2164,12 +2171,19 @@ public: v->visit(this); } - virtual ir_emit_vertex *clone(void *mem_ctx, struct hash_table *) const + virtual ir_emit_vertex *clone(void *mem_ctx, struct hash_table *ht) const { - return new(mem_ctx) ir_emit_vertex(); + return new(mem_ctx) ir_emit_vertex(this->stream->clone(mem_ctx, ht)); } virtual ir_visitor_status accept(ir_hierarchical_visitor *); + + int stream_id() const + { + return stream->as_constant()->value.i[0]; + } + + ir_rvalue *stream; }; /** @@ -2178,9 +2192,11 @@ public: */ class ir_end_primitive : public ir_instruction { public: - ir_end_primitive() - : ir_instruction(ir_type_end_primitive) + ir_end_primitive(ir_rvalue *stream) + : ir_instruction(ir_type_end_primitive), + stream(stream) { + assert(stream); } virtual void accept(ir_visitor *v) @@ -2188,12 +2204,19 @@ public: v->visit(this); } - virtual ir_end_primitive *clone(void *mem_ctx, struct hash_table *) const + virtual ir_end_primitive *clone(void *mem_ctx, struct hash_table *ht) const { - return new(mem_ctx) ir_end_primitive(); + return new(mem_ctx) ir_end_primitive(this->stream->clone(mem_ctx, ht)); } virtual ir_visitor_status accept(ir_hierarchical_visitor *); + + int stream_id() const + { + return stream->as_constant()->value.i[0]; + } + + ir_rvalue *stream; }; /*@}*/ diff --git a/mesalib/src/glsl/ir_hierarchical_visitor.cpp b/mesalib/src/glsl/ir_hierarchical_visitor.cpp index 2e606dda4..d3c00ecdb 100644 --- a/mesalib/src/glsl/ir_hierarchical_visitor.cpp +++ b/mesalib/src/glsl/ir_hierarchical_visitor.cpp @@ -69,24 +69,6 @@ ir_hierarchical_visitor::visit(ir_loop_jump *ir) } ir_visitor_status -ir_hierarchical_visitor::visit(ir_emit_vertex *ir) -{ - if (this->callback != NULL) - this->callback(ir, this->data); - - return visit_continue; -} - -ir_visitor_status -ir_hierarchical_visitor::visit(ir_end_primitive *ir) -{ - if (this->callback != NULL) - this->callback(ir, this->data); - - return visit_continue; -} - -ir_visitor_status ir_hierarchical_visitor::visit(ir_dereference_variable *ir) { if (this->callback != NULL) @@ -303,6 +285,38 @@ ir_hierarchical_visitor::visit_leave(ir_if *ir) return visit_continue; } +ir_visitor_status +ir_hierarchical_visitor::visit_enter(ir_emit_vertex *ir) +{ + if (this->callback != NULL) + this->callback(ir, this->data); + + return visit_continue; +} + +ir_visitor_status +ir_hierarchical_visitor::visit_leave(ir_emit_vertex *ir) +{ + (void) ir; + return visit_continue; +} + +ir_visitor_status +ir_hierarchical_visitor::visit_enter(ir_end_primitive *ir) +{ + if (this->callback != NULL) + this->callback(ir, this->data); + + return visit_continue; +} + +ir_visitor_status +ir_hierarchical_visitor::visit_leave(ir_end_primitive *ir) +{ + (void) ir; + return visit_continue; +} + void ir_hierarchical_visitor::run(exec_list *instructions) { diff --git a/mesalib/src/glsl/ir_hierarchical_visitor.h b/mesalib/src/glsl/ir_hierarchical_visitor.h index 647d2e002..bc89a04d8 100644 --- a/mesalib/src/glsl/ir_hierarchical_visitor.h +++ b/mesalib/src/glsl/ir_hierarchical_visitor.h @@ -87,8 +87,6 @@ public: virtual ir_visitor_status visit(class ir_variable *); virtual ir_visitor_status visit(class ir_constant *); virtual ir_visitor_status visit(class ir_loop_jump *); - virtual ir_visitor_status visit(class ir_emit_vertex *); - virtual ir_visitor_status visit(class ir_end_primitive *); /** * ir_dereference_variable isn't technically a leaf, but it is treated as a @@ -137,6 +135,10 @@ public: virtual ir_visitor_status visit_leave(class ir_discard *); virtual ir_visitor_status visit_enter(class ir_if *); virtual ir_visitor_status visit_leave(class ir_if *); + virtual ir_visitor_status visit_enter(class ir_emit_vertex *); + virtual ir_visitor_status visit_leave(class ir_emit_vertex *); + virtual ir_visitor_status visit_enter(class ir_end_primitive *); + virtual ir_visitor_status visit_leave(class ir_end_primitive *); /*@}*/ diff --git a/mesalib/src/glsl/ir_hv_accept.cpp b/mesalib/src/glsl/ir_hv_accept.cpp index 2a1f70e5b..3ca7a5887 100644 --- a/mesalib/src/glsl/ir_hv_accept.cpp +++ b/mesalib/src/glsl/ir_hv_accept.cpp @@ -405,12 +405,28 @@ ir_if::accept(ir_hierarchical_visitor *v) ir_visitor_status ir_emit_vertex::accept(ir_hierarchical_visitor *v) { - return v->visit(this); + ir_visitor_status s = v->visit_enter(this); + if (s != visit_continue) + return (s == visit_continue_with_parent) ? visit_continue : s; + + s = this->stream->accept(v); + if (s != visit_continue) + return (s == visit_continue_with_parent) ? visit_continue : s; + + return (s == visit_stop) ? s : v->visit_leave(this); } ir_visitor_status ir_end_primitive::accept(ir_hierarchical_visitor *v) { - return v->visit(this); + ir_visitor_status s = v->visit_enter(this); + if (s != visit_continue) + return (s == visit_continue_with_parent) ? visit_continue : s; + + s = this->stream->accept(v); + if (s != visit_continue) + return (s == visit_continue_with_parent) ? visit_continue : s; + + return (s == visit_stop) ? s : v->visit_leave(this); } diff --git a/mesalib/src/glsl/ir_print_visitor.cpp b/mesalib/src/glsl/ir_print_visitor.cpp index c4a6f9c9f..72ad4223f 100644 --- a/mesalib/src/glsl/ir_print_visitor.cpp +++ b/mesalib/src/glsl/ir_print_visitor.cpp @@ -169,11 +169,14 @@ void ir_print_visitor::visit(ir_variable *ir) "in ", "out ", "inout ", "const_in ", "sys ", "temporary " }; STATIC_ASSERT(ARRAY_SIZE(mode) == ir_var_mode_count); + const char *const stream [] = {"", "stream1 ", "stream2 ", "stream3 "}; const char *const interp[] = { "", "smooth", "flat", "noperspective" }; STATIC_ASSERT(ARRAY_SIZE(interp) == INTERP_QUALIFIER_COUNT); - fprintf(f, "(%s%s%s%s%s) ", - cent, samp, inv, mode[ir->data.mode], interp[ir->data.interpolation]); + fprintf(f, "(%s%s%s%s%s%s) ", + cent, samp, inv, mode[ir->data.mode], + stream[ir->data.stream], + interp[ir->data.interpolation]); print_type(f, ir->type); fprintf(f, " %s)", unique_name(ir)); @@ -560,13 +563,18 @@ ir_print_visitor::visit(ir_loop_jump *ir) } void -ir_print_visitor::visit(ir_emit_vertex *) +ir_print_visitor::visit(ir_emit_vertex *ir) { - fprintf(f, "(emit-vertex)"); + fprintf(f, "(emit-vertex "); + ir->stream->accept(this); + fprintf(f, ")\n"); } void -ir_print_visitor::visit(ir_end_primitive *) +ir_print_visitor::visit(ir_end_primitive *ir) { - fprintf(f, "(end-primitive)"); + fprintf(f, "(end-primitive "); + ir->stream->accept(this); + fprintf(f, ")\n"); + } diff --git a/mesalib/src/glsl/ir_reader.cpp b/mesalib/src/glsl/ir_reader.cpp index 28923f3b8..a178c82b5 100644 --- a/mesalib/src/glsl/ir_reader.cpp +++ b/mesalib/src/glsl/ir_reader.cpp @@ -437,6 +437,12 @@ ir_reader::read_declaration(s_expression *expr) var->data.mode = ir_var_function_inout; } else if (strcmp(qualifier->value(), "temporary") == 0) { var->data.mode = ir_var_temporary; + } else if (strcmp(qualifier->value(), "stream1") == 0) { + var->data.stream = 1; + } else if (strcmp(qualifier->value(), "stream2") == 0) { + var->data.stream = 2; + } else if (strcmp(qualifier->value(), "stream3") == 0) { + var->data.stream = 3; } else if (strcmp(qualifier->value(), "smooth") == 0) { var->data.interpolation = INTERP_QUALIFIER_SMOOTH; } else if (strcmp(qualifier->value(), "flat") == 0) { @@ -1109,10 +1115,17 @@ ir_reader::read_texture(s_expression *expr) ir_emit_vertex * ir_reader::read_emit_vertex(s_expression *expr) { - s_pattern pat[] = { "emit-vertex" }; + s_expression *s_stream = NULL; + + s_pattern pat[] = { "emit-vertex", s_stream }; if (MATCH(expr, pat)) { - return new(mem_ctx) ir_emit_vertex(); + ir_rvalue *stream = read_dereference(s_stream); + if (stream == NULL) { + ir_read_error(NULL, "when reading stream info in emit-vertex"); + return NULL; + } + return new(mem_ctx) ir_emit_vertex(stream); } ir_read_error(NULL, "when reading emit-vertex"); return NULL; @@ -1121,10 +1134,17 @@ ir_reader::read_emit_vertex(s_expression *expr) ir_end_primitive * ir_reader::read_end_primitive(s_expression *expr) { - s_pattern pat[] = { "end-primitive" }; + s_expression *s_stream = NULL; + + s_pattern pat[] = { "end-primitive", s_stream }; if (MATCH(expr, pat)) { - return new(mem_ctx) ir_end_primitive(); + ir_rvalue *stream = read_dereference(s_stream); + if (stream == NULL) { + ir_read_error(NULL, "when reading stream info in end-primitive"); + return NULL; + } + return new(mem_ctx) ir_end_primitive(stream); } ir_read_error(NULL, "when reading end-primitive"); return NULL; diff --git a/mesalib/src/glsl/ir_rvalue_visitor.cpp b/mesalib/src/glsl/ir_rvalue_visitor.cpp index fcbe9448d..0370170b3 100644 --- a/mesalib/src/glsl/ir_rvalue_visitor.cpp +++ b/mesalib/src/glsl/ir_rvalue_visitor.cpp @@ -149,6 +149,19 @@ ir_rvalue_base_visitor::rvalue_visit(ir_if *ir) return visit_continue; } +ir_visitor_status +ir_rvalue_base_visitor::rvalue_visit(ir_emit_vertex *ir) +{ + handle_rvalue(&ir->stream); + return visit_continue; +} + +ir_visitor_status +ir_rvalue_base_visitor::rvalue_visit(ir_end_primitive *ir) +{ + handle_rvalue(&ir->stream); + return visit_continue; +} ir_visitor_status ir_rvalue_visitor::visit_leave(ir_expression *ir) @@ -205,6 +218,18 @@ ir_rvalue_visitor::visit_leave(ir_if *ir) } ir_visitor_status +ir_rvalue_visitor::visit_leave(ir_emit_vertex *ir) +{ + return rvalue_visit(ir); +} + +ir_visitor_status +ir_rvalue_visitor::visit_leave(ir_end_primitive *ir) +{ + return rvalue_visit(ir); +} + +ir_visitor_status ir_rvalue_enter_visitor::visit_enter(ir_expression *ir) { return rvalue_visit(ir); @@ -257,3 +282,15 @@ ir_rvalue_enter_visitor::visit_enter(ir_if *ir) { return rvalue_visit(ir); } + +ir_visitor_status +ir_rvalue_enter_visitor::visit_enter(ir_emit_vertex *ir) +{ + return rvalue_visit(ir); +} + +ir_visitor_status +ir_rvalue_enter_visitor::visit_enter(ir_end_primitive *ir) +{ + return rvalue_visit(ir); +} diff --git a/mesalib/src/glsl/ir_rvalue_visitor.h b/mesalib/src/glsl/ir_rvalue_visitor.h index 2179fa5a8..04ec0fa39 100644 --- a/mesalib/src/glsl/ir_rvalue_visitor.h +++ b/mesalib/src/glsl/ir_rvalue_visitor.h @@ -41,6 +41,8 @@ public: ir_visitor_status rvalue_visit(ir_return *); ir_visitor_status rvalue_visit(ir_swizzle *); ir_visitor_status rvalue_visit(ir_texture *); + ir_visitor_status rvalue_visit(ir_emit_vertex *); + ir_visitor_status rvalue_visit(ir_end_primitive *); virtual void handle_rvalue(ir_rvalue **rvalue) = 0; }; @@ -57,6 +59,8 @@ public: virtual ir_visitor_status visit_leave(ir_return *); virtual ir_visitor_status visit_leave(ir_swizzle *); virtual ir_visitor_status visit_leave(ir_texture *); + virtual ir_visitor_status visit_leave(ir_emit_vertex *); + virtual ir_visitor_status visit_leave(ir_end_primitive *); }; class ir_rvalue_enter_visitor : public ir_rvalue_base_visitor { @@ -71,4 +75,6 @@ public: virtual ir_visitor_status visit_enter(ir_return *); virtual ir_visitor_status visit_enter(ir_swizzle *); virtual ir_visitor_status visit_enter(ir_texture *); + virtual ir_visitor_status visit_enter(ir_emit_vertex *); + virtual ir_visitor_status visit_enter(ir_end_primitive *); }; diff --git a/mesalib/src/glsl/link_atomics.cpp b/mesalib/src/glsl/link_atomics.cpp index d92cdb117..fbe4e7364 100644 --- a/mesalib/src/glsl/link_atomics.cpp +++ b/mesalib/src/glsl/link_atomics.cpp @@ -54,9 +54,18 @@ namespace { void push_back(unsigned id, ir_variable *var) { - counters = (active_atomic_counter *) - realloc(counters, sizeof(active_atomic_counter) * (num_counters + 1)); + active_atomic_counter *new_counters; + new_counters = (active_atomic_counter *) + realloc(counters, sizeof(active_atomic_counter) * + (num_counters + 1)); + + if (new_counters == NULL) { + _mesa_error_no_memory(__func__); + return; + } + + counters = new_counters; counters[num_counters].id = id; counters[num_counters].var = var; num_counters++; diff --git a/mesalib/src/glsl/link_uniform_blocks.cpp b/mesalib/src/glsl/link_uniform_blocks.cpp index 1a0e64318..53a18c934 100644 --- a/mesalib/src/glsl/link_uniform_blocks.cpp +++ b/mesalib/src/glsl/link_uniform_blocks.cpp @@ -170,6 +170,12 @@ link_uniform_blocks(void *mem_ctx, struct hash_table *block_hash = _mesa_hash_table_create(mem_ctx, _mesa_key_string_equal); + if (block_hash == NULL) { + _mesa_error_no_memory(__func__); + linker_error(prog, "out of memory\n"); + return 0; + } + /* Determine which uniform blocks are active. */ link_uniform_block_active_visitor v(mem_ctx, block_hash, prog); diff --git a/mesalib/src/glsl/link_varyings.cpp b/mesalib/src/glsl/link_varyings.cpp index 686329857..520a51a27 100644 --- a/mesalib/src/glsl/link_varyings.cpp +++ b/mesalib/src/glsl/link_varyings.cpp @@ -291,6 +291,7 @@ tfeedback_decl::init(struct gl_context *ctx, const void *mem_ctx, this->skip_components = 0; this->next_buffer_separator = false; this->matched_candidate = NULL; + this->stream_id = 0; if (ctx->Extensions.ARB_transform_feedback3) { /* Parse gl_NextBuffer. */ @@ -355,8 +356,8 @@ tfeedback_decl::is_same(const tfeedback_decl &x, const tfeedback_decl &y) /** - * Assign a location for this tfeedback_decl object based on the transform - * feedback candidate found by find_candidate. + * Assign a location and stream ID for this tfeedback_decl object based on the + * transform feedback candidate found by find_candidate. * * If an error occurs, the error is reported through linker_error() and false * is returned. @@ -437,6 +438,11 @@ tfeedback_decl::assign_location(struct gl_context *ctx, return false; } + /* Only transform feedback varyings can be assigned to non-zero streams, + * so assign the stream id here. + */ + this->stream_id = this->matched_candidate->toplevel_var->data.stream; + return true; } @@ -495,6 +501,7 @@ tfeedback_decl::store(struct gl_context *ctx, struct gl_shader_program *prog, info->Outputs[info->NumOutputs].ComponentOffset = location_frac; info->Outputs[info->NumOutputs].OutputRegister = location; info->Outputs[info->NumOutputs].NumComponents = output_size; + info->Outputs[info->NumOutputs].StreamId = stream_id; info->Outputs[info->NumOutputs].OutputBuffer = buffer; info->Outputs[info->NumOutputs].DstOffset = info->BufferStride[buffer]; ++info->NumOutputs; @@ -628,10 +635,27 @@ store_tfeedback_info(struct gl_context *ctx, struct gl_shader_program *prog, } else { /* GL_INVERLEAVED_ATTRIBS */ + int buffer_stream_id = -1; for (unsigned i = 0; i < num_tfeedback_decls; ++i) { if (tfeedback_decls[i].is_next_buffer_separator()) { num_buffers++; + buffer_stream_id = -1; continue; + } else if (buffer_stream_id == -1) { + /* First varying writing to this buffer: remember its stream */ + buffer_stream_id = (int) tfeedback_decls[i].get_stream_id(); + } else if (buffer_stream_id != + (int) tfeedback_decls[i].get_stream_id()) { + /* Varying writes to the same buffer from a different stream */ + linker_error(prog, + "Transform feedback can't capture varyings belonging " + "to different vertex streams in a single buffer. " + "Varying %s writes to buffer from stream %u, other " + "varyings in the same buffer write from stream %u.", + tfeedback_decls[i].name(), + tfeedback_decls[i].get_stream_id(), + buffer_stream_id); + return false; } if (!tfeedback_decls[i].store(ctx, prog, @@ -1323,6 +1347,11 @@ assign_varying_locations(struct gl_context *ctx, (output_var->data.mode != ir_var_shader_out)) continue; + /* Only geometry shaders can use non-zero streams */ + assert(output_var->data.stream == 0 || + (output_var->data.stream < MAX_VERTEX_STREAMS && + producer->Stage == MESA_SHADER_GEOMETRY)); + tfeedback_candidate_generator g(mem_ctx, tfeedback_candidates); g.process(output_var); @@ -1338,6 +1367,14 @@ assign_varying_locations(struct gl_context *ctx, if (input_var || (prog->SeparateShader && consumer == NULL)) { matches.record(output_var, input_var); } + + /* Only stream 0 outputs can be consumed in the next stage */ + if (input_var && output_var->data.stream != 0) { + linker_error(prog, "output %s is assigned to stream=%d but " + "is linked to an input, which requires stream=0", + output_var->name, output_var->data.stream); + return false; + } } } else { /* If there's no producer stage, then this must be a separable program. diff --git a/mesalib/src/glsl/link_varyings.h b/mesalib/src/glsl/link_varyings.h index 6fa268176..afc16a8ba 100644 --- a/mesalib/src/glsl/link_varyings.h +++ b/mesalib/src/glsl/link_varyings.h @@ -112,6 +112,16 @@ public: return !this->next_buffer_separator && !this->skip_components; } + const char *name() const + { + return this->orig_name; + } + + unsigned get_stream_id() const + { + return this->stream_id; + } + /** * The total number of varying components taken up by this variable. Only * valid if assign_location() has been called. @@ -210,6 +220,13 @@ private: * data structure that was found. Otherwise NULL. */ const tfeedback_candidate *matched_candidate; + + /** + * StreamId assigned to this varying (defaults to 0). Can only be set to + * values other than 0 in geometry shaders that use the stream layout + * modifier. Accepted values must be in the range [0, MAX_VERTEX_STREAMS-1]. + */ + unsigned stream_id; }; diff --git a/mesalib/src/glsl/linker.cpp b/mesalib/src/glsl/linker.cpp index 0b6a71679..3036ebcb3 100644 --- a/mesalib/src/glsl/linker.cpp +++ b/mesalib/src/glsl/linker.cpp @@ -250,31 +250,100 @@ public: } }; - /** - * Visitor that determines whether or not a shader uses ir_end_primitive. + * Visitor that determines the highest stream id to which a (geometry) shader + * emits vertices. It also checks whether End{Stream}Primitive is ever called. */ -class find_end_primitive_visitor : public ir_hierarchical_visitor { +class find_emit_vertex_visitor : public ir_hierarchical_visitor { public: - find_end_primitive_visitor() - : found(false) + find_emit_vertex_visitor(int max_allowed) + : max_stream_allowed(max_allowed), + invalid_stream_id(0), + invalid_stream_id_from_emit_vertex(false), + end_primitive_found(false), + uses_non_zero_stream(false) { /* empty */ } - virtual ir_visitor_status visit(ir_end_primitive *) + virtual ir_visitor_status visit_leave(ir_emit_vertex *ir) { - found = true; - return visit_stop; + int stream_id = ir->stream_id(); + + if (stream_id < 0) { + invalid_stream_id = stream_id; + invalid_stream_id_from_emit_vertex = true; + return visit_stop; + } + + if (stream_id > max_stream_allowed) { + invalid_stream_id = stream_id; + invalid_stream_id_from_emit_vertex = true; + return visit_stop; + } + + if (stream_id != 0) + uses_non_zero_stream = true; + + return visit_continue; } - bool end_primitive_found() + virtual ir_visitor_status visit_leave(ir_end_primitive *ir) { - return found; + end_primitive_found = true; + + int stream_id = ir->stream_id(); + + if (stream_id < 0) { + invalid_stream_id = stream_id; + invalid_stream_id_from_emit_vertex = false; + return visit_stop; + } + + if (stream_id > max_stream_allowed) { + invalid_stream_id = stream_id; + invalid_stream_id_from_emit_vertex = false; + return visit_stop; + } + + if (stream_id != 0) + uses_non_zero_stream = true; + + return visit_continue; + } + + bool error() + { + return invalid_stream_id != 0; + } + + const char *error_func() + { + return invalid_stream_id_from_emit_vertex ? + "EmitStreamVertex" : "EndStreamPrimitive"; + } + + int error_stream() + { + return invalid_stream_id; + } + + bool uses_streams() + { + return uses_non_zero_stream; + } + + bool uses_end_primitive() + { + return end_primitive_found; } private: - bool found; + int max_stream_allowed; + int invalid_stream_id; + bool invalid_stream_id_from_emit_vertex; + bool end_primitive_found; + bool uses_non_zero_stream; }; } /* anonymous namespace */ @@ -551,10 +620,58 @@ validate_geometry_shader_executable(struct gl_shader_program *prog, analyze_clip_usage(prog, shader, &prog->Geom.UsesClipDistance, &prog->Geom.ClipDistanceArraySize); +} + +/** + * Check if geometry shaders emit to non-zero streams and do corresponding + * validations. + */ +static void +validate_geometry_shader_emissions(struct gl_context *ctx, + struct gl_shader_program *prog) +{ + if (prog->_LinkedShaders[MESA_SHADER_GEOMETRY] != NULL) { + find_emit_vertex_visitor emit_vertex(ctx->Const.MaxVertexStreams - 1); + emit_vertex.run(prog->_LinkedShaders[MESA_SHADER_GEOMETRY]->ir); + if (emit_vertex.error()) { + linker_error(prog, "Invalid call %s(%d). Accepted values for the " + "stream parameter are in the range [0, %d].", + emit_vertex.error_func(), + emit_vertex.error_stream(), + ctx->Const.MaxVertexStreams - 1); + } + prog->Geom.UsesStreams = emit_vertex.uses_streams(); + prog->Geom.UsesEndPrimitive = emit_vertex.uses_end_primitive(); - find_end_primitive_visitor end_primitive; - end_primitive.run(shader->ir); - prog->Geom.UsesEndPrimitive = end_primitive.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. Currently we only set prog->Geom.UsesStreams to TRUE when + * EmitStreamVertex() or EmitEndPrimitive() are called with a non-zero + * stream. + */ + if (prog->Geom.UsesStreams && prog->Geom.OutputType != GL_POINTS) { + linker_error(prog, "EmitStreamVertex(n) and EndStreamPrimitive(n) " + "with n>0 requires point output"); + } + } } @@ -1479,6 +1596,8 @@ link_intrastage_shaders(void *mem_ctx, const unsigned num_uniform_blocks = link_uniform_blocks(mem_ctx, prog, shader_list, num_shaders, &uniform_blocks); + if (!prog->LinkStatus) + return NULL; /* Check that there is only a single definition of each function signature * across all shaders. @@ -2556,6 +2675,9 @@ link_shaders(struct gl_context *ctx, struct gl_shader_program *prog) ; } + /* Check and validate stream emissions in geometry shaders */ + validate_geometry_shader_emissions(ctx, prog); + /* Mark all generic shader inputs and outputs as unpaired. */ for (unsigned i = MESA_SHADER_VERTEX; i <= MESA_SHADER_FRAGMENT; i++) { if (prog->_LinkedShaders[i] != NULL) { diff --git a/mesalib/src/glsl/lower_output_reads.cpp b/mesalib/src/glsl/lower_output_reads.cpp index afe17766b..1ee815d5e 100644 --- a/mesalib/src/glsl/lower_output_reads.cpp +++ b/mesalib/src/glsl/lower_output_reads.cpp @@ -52,7 +52,7 @@ public: output_read_remover(); ~output_read_remover(); virtual ir_visitor_status visit(class ir_dereference_variable *); - virtual ir_visitor_status visit(class ir_emit_vertex *); + virtual ir_visitor_status visit_leave(class ir_emit_vertex *); virtual ir_visitor_status visit_leave(class ir_return *); virtual ir_visitor_status visit_leave(class ir_function_signature *); }; @@ -148,7 +148,7 @@ output_read_remover::visit_leave(ir_return *ir) } ir_visitor_status -output_read_remover::visit(ir_emit_vertex *ir) +output_read_remover::visit_leave(ir_emit_vertex *ir) { hash_table_call_foreach(replacements, emit_return_copy, ir); hash_table_clear(replacements); diff --git a/mesalib/src/glsl/lower_packed_varyings.cpp b/mesalib/src/glsl/lower_packed_varyings.cpp index e8654748f..eda56a97b 100644 --- a/mesalib/src/glsl/lower_packed_varyings.cpp +++ b/mesalib/src/glsl/lower_packed_varyings.cpp @@ -613,7 +613,7 @@ public: explicit lower_packed_varyings_gs_splicer(void *mem_ctx, const exec_list *instructions); - virtual ir_visitor_status visit(ir_emit_vertex *ev); + virtual ir_visitor_status visit_leave(ir_emit_vertex *ev); private: /** @@ -637,7 +637,7 @@ lower_packed_varyings_gs_splicer::lower_packed_varyings_gs_splicer( ir_visitor_status -lower_packed_varyings_gs_splicer::visit(ir_emit_vertex *ev) +lower_packed_varyings_gs_splicer::visit_leave(ir_emit_vertex *ev) { foreach_list(node, this->instructions) { ir_instruction *ir = (ir_instruction *) node; diff --git a/mesalib/src/glsl/opt_dead_code_local.cpp b/mesalib/src/glsl/opt_dead_code_local.cpp index c27c526f9..88895fb0e 100644 --- a/mesalib/src/glsl/opt_dead_code_local.cpp +++ b/mesalib/src/glsl/opt_dead_code_local.cpp @@ -114,7 +114,7 @@ public: return visit_continue_with_parent; } - virtual ir_visitor_status visit(ir_emit_vertex *) + virtual ir_visitor_status visit_leave(ir_emit_vertex *) { /* For the purpose of dead code elimination, emitting a vertex counts as * "reading" all of the currently assigned output variables. |