}
};
-
/**
- * 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 */
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");
+ }
+ }
}
;
}
+ /* 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) {