• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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