1/* 2 * Copyright © 2020 Google, Inc. 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining a 5 * copy of this software and associated documentation files (the "Software"), 6 * to deal in the Software without restriction, including without limitation 7 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 * and/or sell copies of the Software, and to permit persons to whom the 9 * Software is furnished to do so, subject to the following conditions: 10 * 11 * The above copyright notice and this permission notice (including the next 12 * paragraph) shall be included in all copies or substantial portions of the 13 * Software. 14 * 15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 * SOFTWARE. 22 */ 23 24#include "pipe/p_state.h" 25#include "util/u_prim.h" 26 27#include "freedreno_batch.h" 28#include "freedreno_gmem.h" 29 30#include "fd6_vsc.h" 31 32/* 33 * Helper util to update expected vsc draw and primitive stream sizes, see 34 * https://github.com/freedreno/freedreno/wiki/Visibility-Stream-Format 35 */ 36 37enum bits_per { 38 byte = 8, 39 dword = 4 * byte, 40}; 41 42/** 43 * Determine # of bits required to store a given number, see 44 * https://github.com/freedreno/freedreno/wiki/Visibility-Stream-Format#numbers 45 */ 46static unsigned 47number_size_bits(unsigned nr) 48{ 49 unsigned n = util_last_bit(nr); 50 assert(n); /* encoding 0 is not possible */ 51 return n + (n - 1); 52} 53 54/** 55 * Determine # of bits requred to store a given bitfield, see 56 * https://github.com/freedreno/freedreno/wiki/Visibility-Stream-Format#bitfields 57 */ 58static unsigned 59bitfield_size_bits(unsigned n) 60{ 61 return n + 1; /* worst case is always 1 + nr of bits */ 62} 63 64static unsigned 65prim_count(const struct pipe_draw_info *info, 66 const struct pipe_draw_start_count_bias *draw) 67{ 68 /* PIPE_PRIM_MAX used internally for RECTLIST blits on 3d pipe: */ 69 unsigned vtx_per_prim = 70 (info->mode == PIPE_PRIM_MAX) ? 2 : u_vertices_per_prim(info->mode); 71 return MAX2(1, (draw->count * info->instance_count) / vtx_per_prim); 72} 73 74/** 75 * The primitive stream uses a run-length encoding, where each packet contains a 76 * bitfield of bins covered and then the number of primitives which have the 77 * same bitfield. Each packet consists of the following, in order: 78 * 79 * - The (compressed) bitfield of bins covered 80 * - The number of primitives with this bitset 81 * - Checksum 82 * 83 * The worst case would be that each primitive has a different bitmask. In 84 * practice, assuming ever other primitive has a different bitmask still gets us 85 * conservatively large primitive stream sizes. (Ie. 10x what is needed, vs. 86 * 20x) 87 * 88 * https://github.com/freedreno/freedreno/wiki/Visibility-Stream-Format#primitive-streams 89 */ 90static unsigned 91primitive_stream_size_bits(const struct pipe_draw_info *info, 92 const struct pipe_draw_start_count_bias *draw, 93 unsigned num_bins) 94{ 95 unsigned num_prims = prim_count(info, draw); 96 unsigned nbits = 97 (bitfield_size_bits(num_bins) /* bitfield of bins covered */ 98 + number_size_bits(1) /* number of primitives with this bitset */ 99 + 1 /* checksum */ 100 ) * 101 DIV_ROUND_UP(num_prims, 2); 102 return align(nbits, dword); 103} 104 105/** 106 * Each draw stream packet contains the following: 107 * 108 * - Bin bitfield 109 * - Last instance bit 110 * - If bitfield is empty, the number of draws it is empty for, otherwise 111 * the size of the corresponding primitive stream in DWORD's. 112 * - Checksum 113 * 114 * https://github.com/freedreno/freedreno/wiki/Visibility-Stream-Format#draw-streams 115 */ 116static unsigned 117draw_stream_size_bits(const struct pipe_draw_info *info, unsigned num_bins, 118 unsigned prim_strm_bits) 119{ 120 unsigned ndwords = prim_strm_bits / dword; 121 return (bitfield_size_bits(num_bins) /* bitfield of bins */ 122 + 1 /* last-instance-bit */ 123 + number_size_bits(ndwords) /* size of corresponding prim strm */ 124 + 1 /* checksum */ 125 ) * 126 MAX2(1, info->instance_count); 127} 128 129void 130fd6_vsc_update_sizes(struct fd_batch *batch, const struct pipe_draw_info *info, 131 const struct pipe_draw_start_count_bias *draw) 132{ 133 if (!batch->num_bins_per_pipe) { 134 batch->num_bins_per_pipe = fd_gmem_estimate_bins_per_pipe(batch); 135 136 /* This is a convenient spot to add the size of the final draw- 137 * stream packet: 138 * 139 * If there are N bins, the final packet, after all the draws are 140 * done, consists of a 1 followed by N + 17 0's, plus a final 1. 141 * This uses the otherwise-unused pattern of a non-empty bitfield 142 * (initial 1) that is nontheless empty (has all 0's) 143 */ 144 unsigned final_pkt_sz = 1 + batch->num_bins_per_pipe + 17 + 1; 145 batch->prim_strm_bits = align(final_pkt_sz, dword); 146 } 147 148 unsigned prim_strm_bits = 149 primitive_stream_size_bits(info, draw, batch->num_bins_per_pipe); 150 unsigned draw_strm_bits = 151 draw_stream_size_bits(info, batch->num_bins_per_pipe, prim_strm_bits); 152 153#if 0 154 mesa_logd("vsc: prim_strm_bits=%d, draw_strm_bits=%d, nb=%u, ic=%u, c=%u, pc=%u (%s)", 155 prim_strm_bits, draw_strm_bits, batch->num_bins_per_pipe, 156 info->instance_count, info->count, 157 (info->count * info->instance_count) / 158 u_vertices_per_prim(info->mode), 159 u_prim_name(info->mode)); 160#endif 161 162 batch->prim_strm_bits += prim_strm_bits; 163 batch->draw_strm_bits += draw_strm_bits; 164} 165