1 /*
2 * Copyright © 2020 Google, Inc.
3 * SPDX-License-Identifier: MIT
4 */
5
6 #define FD_BO_NO_HARDPIN 1
7
8 #include "pipe/p_state.h"
9
10 #include "freedreno_batch.h"
11 #include "freedreno_gmem.h"
12
13 #include "fd6_vsc.h"
14
15 /*
16 * Helper util to update expected vsc draw and primitive stream sizes, see
17 * https://gitlab.freedesktop.org/freedreno/freedreno/-/wikis/Visibility-Stream-Format
18 */
19
20 enum bits_per {
21 byte = 8,
22 dword = 4 * byte,
23 };
24
25 /**
26 * Determine # of bits required to store a given number, see
27 * https://gitlab.freedesktop.org/freedreno/freedreno/-/wikis/Visibility-Stream-Format#numbers
28 */
29 static unsigned
number_size_bits(unsigned nr)30 number_size_bits(unsigned nr)
31 {
32 unsigned n = util_last_bit(nr);
33 assert(n); /* encoding 0 is not possible */
34 return n + (n - 1);
35 }
36
37 /**
38 * Determine # of bits requred to store a given bitfield, see
39 * https://gitlab.freedesktop.org/freedreno/freedreno/-/wikis/Visibility-Stream-Format#bitfields
40 */
41 static unsigned
bitfield_size_bits(unsigned n)42 bitfield_size_bits(unsigned n)
43 {
44 return n + 1; /* worst case is always 1 + nr of bits */
45 }
46
47 static unsigned
prim_count(const struct pipe_draw_info * info,const struct pipe_draw_start_count_bias * draw)48 prim_count(const struct pipe_draw_info *info,
49 const struct pipe_draw_start_count_bias *draw)
50 {
51 /* MESA_PRIM_COUNT used internally for RECTLIST blits on 3d pipe: */
52 unsigned vtx_per_prim =
53 (info->mode == MESA_PRIM_COUNT) ? 2 : mesa_vertices_per_prim(info->mode);
54 return MAX2(1, (draw->count * info->instance_count) / vtx_per_prim);
55 }
56
57 /**
58 * The primitive stream uses a run-length encoding, where each packet contains a
59 * bitfield of bins covered and then the number of primitives which have the
60 * same bitfield. Each packet consists of the following, in order:
61 *
62 * - The (compressed) bitfield of bins covered
63 * - The number of primitives with this bitset
64 * - Checksum
65 *
66 * The worst case would be that each primitive has a different bitmask. In
67 * practice, assuming ever other primitive has a different bitmask still gets us
68 * conservatively large primitive stream sizes. (Ie. 10x what is needed, vs.
69 * 20x)
70 *
71 * https://gitlab.freedesktop.org/freedreno/freedreno/-/wikis/Visibility-Stream-Format#primitive-streams
72 */
73 static unsigned
primitive_stream_size_bits(const struct pipe_draw_info * info,const struct pipe_draw_start_count_bias * draw,unsigned num_bins)74 primitive_stream_size_bits(const struct pipe_draw_info *info,
75 const struct pipe_draw_start_count_bias *draw,
76 unsigned num_bins)
77 {
78 unsigned num_prims = prim_count(info, draw);
79 unsigned nbits =
80 (bitfield_size_bits(num_bins) /* bitfield of bins covered */
81 + number_size_bits(1) /* number of primitives with this bitset */
82 + 1 /* checksum */
83 ) *
84 DIV_ROUND_UP(num_prims, 2);
85 return align(nbits, dword);
86 }
87
88 /**
89 * Each draw stream packet contains the following:
90 *
91 * - Bin bitfield
92 * - Last instance bit
93 * - If bitfield is empty, the number of draws it is empty for, otherwise
94 * the size of the corresponding primitive stream in DWORD's.
95 * - Checksum
96 *
97 * https://gitlab.freedesktop.org/freedreno/freedreno/-/wikis/Visibility-Stream-Format#draw-streams
98 */
99 static unsigned
draw_stream_size_bits(const struct pipe_draw_info * info,unsigned num_bins,unsigned prim_strm_bits)100 draw_stream_size_bits(const struct pipe_draw_info *info, unsigned num_bins,
101 unsigned prim_strm_bits)
102 {
103 unsigned ndwords = prim_strm_bits / dword;
104 return (bitfield_size_bits(num_bins) /* bitfield of bins */
105 + 1 /* last-instance-bit */
106 + number_size_bits(ndwords) /* size of corresponding prim strm */
107 + 1 /* checksum */
108 ) *
109 MAX2(1, info->instance_count);
110 }
111
112 void
fd6_vsc_update_sizes(struct fd_batch * batch,const struct pipe_draw_info * info,const struct pipe_draw_start_count_bias * draw)113 fd6_vsc_update_sizes(struct fd_batch *batch, const struct pipe_draw_info *info,
114 const struct pipe_draw_start_count_bias *draw)
115 {
116 if (!batch->num_bins_per_pipe) {
117 batch->num_bins_per_pipe = fd_gmem_estimate_bins_per_pipe(batch);
118
119 /* This is a convenient spot to add the size of the final draw-
120 * stream packet:
121 *
122 * If there are N bins, the final packet, after all the draws are
123 * done, consists of a 1 followed by N + 17 0's, plus a final 1.
124 * This uses the otherwise-unused pattern of a non-empty bitfield
125 * (initial 1) that is nontheless empty (has all 0's)
126 */
127 unsigned final_pkt_sz = 1 + batch->num_bins_per_pipe + 17 + 1;
128 batch->prim_strm_bits = align(final_pkt_sz, dword);
129 }
130
131 unsigned prim_strm_bits =
132 primitive_stream_size_bits(info, draw, batch->num_bins_per_pipe);
133 unsigned draw_strm_bits =
134 draw_stream_size_bits(info, batch->num_bins_per_pipe, prim_strm_bits);
135
136 #if 0
137 mesa_logd("vsc: prim_strm_bits=%d, draw_strm_bits=%d, nb=%u, ic=%u, c=%u, pc=%u (%s)",
138 prim_strm_bits, draw_strm_bits, batch->num_bins_per_pipe,
139 info->instance_count, info->count,
140 (info->count * info->instance_count) /
141 mesa_vertices_per_prim(info->mode),
142 u_prim_name(info->mode));
143 #endif
144
145 batch->prim_strm_bits += prim_strm_bits;
146 batch->draw_strm_bits += draw_strm_bits;
147 }
148