1 /*
2 * Copyright 2023 Alyssa Rosenzweig
3 * Copyright 2022 Collabora Ltd.
4 * SPDX-License-Identifier: MIT
5 */
6
7 #include "compiler/nir/nir_builder.h"
8 #include "compiler/nir/nir_xfb_info.h"
9 #include "pipe/p_defines.h"
10 #include "util/u_draw.h"
11 #include "util/u_dump.h"
12 #include "util/u_inlines.h"
13 #include "util/u_prim.h"
14 #include "agx_state.h"
15
16 static struct pipe_stream_output_target *
agx_create_stream_output_target(struct pipe_context * pctx,struct pipe_resource * prsc,unsigned buffer_offset,unsigned buffer_size)17 agx_create_stream_output_target(struct pipe_context *pctx,
18 struct pipe_resource *prsc,
19 unsigned buffer_offset, unsigned buffer_size)
20 {
21 struct agx_streamout_target *target =
22 rzalloc(pctx, struct agx_streamout_target);
23
24 if (!target)
25 return NULL;
26
27 pipe_reference_init(&target->base.reference, 1);
28 pipe_resource_reference(&target->base.buffer, prsc);
29
30 target->base.context = pctx;
31 target->base.buffer_offset = buffer_offset;
32 target->base.buffer_size = buffer_size;
33
34 uint32_t zero = 0;
35 target->offset = pipe_buffer_create_with_data(pctx, PIPE_BIND_GLOBAL,
36 PIPE_USAGE_DEFAULT, 4, &zero);
37
38 return &target->base;
39 }
40
41 static void
agx_stream_output_target_destroy(struct pipe_context * pctx,struct pipe_stream_output_target * target)42 agx_stream_output_target_destroy(struct pipe_context *pctx,
43 struct pipe_stream_output_target *target)
44 {
45 struct agx_streamout_target *tgt = agx_so_target(target);
46
47 pipe_resource_reference(&tgt->base.buffer, NULL);
48 pipe_resource_reference(&tgt->offset, NULL);
49 ralloc_free(target);
50 }
51
52 static void
agx_set_stream_output_targets(struct pipe_context * pctx,unsigned num_targets,struct pipe_stream_output_target ** targets,const unsigned * offsets,enum mesa_prim output_prim)53 agx_set_stream_output_targets(struct pipe_context *pctx, unsigned num_targets,
54 struct pipe_stream_output_target **targets,
55 const unsigned *offsets,
56 enum mesa_prim output_prim)
57 {
58 struct agx_context *ctx = agx_context(pctx);
59 struct agx_streamout *so = &ctx->streamout;
60
61 assert(num_targets <= ARRAY_SIZE(so->targets));
62
63 for (unsigned i = 0; i < num_targets; i++) {
64 /* From the Gallium documentation:
65 *
66 * -1 means the buffer should be appended to, and everything else sets
67 * the internal offset.
68 *
69 * We append regardless, so just check for != -1. Yes, using a negative
70 * sentinel value with an unsigned type is bananas. But it's in the
71 * Gallium contract and it will work out fine. Probably should be
72 * redefined to be ~0 instead of -1 but it doesn't really matter.
73 */
74 if (offsets[i] != -1 && targets[i] != NULL) {
75 pipe_buffer_write(pctx, agx_so_target(targets[i])->offset, 0, 4,
76 &offsets[i]);
77 }
78
79 pipe_so_target_reference(&so->targets[i], targets[i]);
80 }
81
82 for (unsigned i = num_targets; i < so->num_targets; i++)
83 pipe_so_target_reference(&so->targets[i], NULL);
84
85 so->num_targets = num_targets;
86 }
87
88 static struct pipe_stream_output_target *
get_target(struct agx_context * ctx,unsigned buffer)89 get_target(struct agx_context *ctx, unsigned buffer)
90 {
91 if (buffer < ctx->streamout.num_targets)
92 return ctx->streamout.targets[buffer];
93 else
94 return NULL;
95 }
96
97 /*
98 * Return the address of the indexed streamout buffer. This will be
99 * pushed into the streamout shader.
100 */
101 uint64_t
agx_batch_get_so_address(struct agx_batch * batch,unsigned buffer,uint32_t * size)102 agx_batch_get_so_address(struct agx_batch *batch, unsigned buffer,
103 uint32_t *size)
104 {
105 struct pipe_stream_output_target *target = get_target(batch->ctx, buffer);
106
107 /* If there's no target, don't write anything */
108 if (!target) {
109 *size = 0;
110 return 0;
111 }
112
113 /* Otherwise, write the target */
114 struct agx_resource *rsrc = agx_resource(target->buffer);
115 agx_batch_writes_range(batch, rsrc, target->buffer_offset,
116 target->buffer_size);
117
118 *size = target->buffer_size;
119 return rsrc->bo->va->addr + target->buffer_offset;
120 }
121
122 void
agx_draw_vbo_from_xfb(struct pipe_context * pctx,const struct pipe_draw_info * info,unsigned drawid_offset,const struct pipe_draw_indirect_info * indirect)123 agx_draw_vbo_from_xfb(struct pipe_context *pctx,
124 const struct pipe_draw_info *info, unsigned drawid_offset,
125 const struct pipe_draw_indirect_info *indirect)
126 {
127 perf_debug_ctx(agx_context(pctx), "draw auto");
128
129 struct agx_streamout_target *so =
130 agx_so_target(indirect->count_from_stream_output);
131
132 unsigned offset_B = 0;
133 pipe_buffer_read(pctx, so->offset, 0, 4, &offset_B);
134
135 unsigned count = offset_B / so->stride;
136
137 struct pipe_draw_start_count_bias draw = {
138 .start = 0,
139 .count = count,
140 };
141
142 pctx->draw_vbo(pctx, info, drawid_offset, NULL, &draw, 1);
143 }
144
145 static uint32_t
xfb_prims_for_vertices(enum mesa_prim mode,unsigned verts)146 xfb_prims_for_vertices(enum mesa_prim mode, unsigned verts)
147 {
148 uint32_t prims = u_decomposed_prims_for_vertices(mode, verts);
149
150 /* The GL spec isn't super clear about this, but it implies that quads are
151 * supposed to be tessellated into primitives and piglit
152 * (ext_transform_feedback-tessellation quads) checks this.
153 */
154 if (u_decomposed_prim(mode) == MESA_PRIM_QUADS)
155 prims *= 2;
156
157 return prims;
158 }
159
160 /*
161 * Count generated primitives on the CPU for transform feedback. This only works
162 * in the absence of indirect draws, geometry shaders, or tessellation.
163 */
164 void
agx_primitives_update_direct(struct agx_context * ctx,const struct pipe_draw_info * info,const struct pipe_draw_start_count_bias * draw)165 agx_primitives_update_direct(struct agx_context *ctx,
166 const struct pipe_draw_info *info,
167 const struct pipe_draw_start_count_bias *draw)
168 {
169 assert(ctx->active_queries && ctx->prims_generated[0] && "precondition");
170 assert(!ctx->stage[PIPE_SHADER_GEOMETRY].shader &&
171 "Geometry shaders use their own counting");
172
173 agx_query_increment_cpu(ctx, ctx->prims_generated[0],
174 xfb_prims_for_vertices(info->mode, draw->count));
175 }
176
177 void
agx_init_streamout_functions(struct pipe_context * ctx)178 agx_init_streamout_functions(struct pipe_context *ctx)
179 {
180 ctx->create_stream_output_target = agx_create_stream_output_target;
181 ctx->stream_output_target_destroy = agx_stream_output_target_destroy;
182 ctx->set_stream_output_targets = agx_set_stream_output_targets;
183 }
184