turnip: specify initial size in tu_cs_init
[mesa.git] / src / freedreno / vulkan / tu_cs.c
1 /*
2 * Copyright © 2019 Google LLC
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
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 * DEALINGS IN THE SOFTWARE.
22 */
23
24 #include "tu_cs.h"
25
26 /**
27 * Initialize a command stream.
28 */
29 void
30 tu_cs_init(struct tu_cs *cs, uint32_t initial_size)
31 {
32 memset(cs, 0, sizeof(*cs));
33
34 cs->next_bo_size = initial_size;
35 }
36
37 /**
38 * Finish and release all resources owned by a command stream.
39 */
40 void
41 tu_cs_finish(struct tu_device *dev, struct tu_cs *cs)
42 {
43 for (uint32_t i = 0; i < cs->bo_count; ++i) {
44 tu_bo_finish(dev, cs->bos[i]);
45 free(cs->bos[i]);
46 }
47
48 free(cs->entries);
49 free(cs->bos);
50 }
51
52 /**
53 * Get the offset of the command packets emitted since the last call to
54 * tu_cs_add_entry.
55 */
56 static uint32_t
57 tu_cs_get_offset(const struct tu_cs *cs)
58 {
59 assert(cs->bo_count);
60 return cs->start - (uint32_t *) cs->bos[cs->bo_count - 1]->map;
61 }
62
63 /**
64 * Get the size of the command packets emitted since the last call to
65 * tu_cs_add_entry.
66 */
67 static uint32_t
68 tu_cs_get_size(const struct tu_cs *cs)
69 {
70 return cs->cur - cs->start;
71 }
72
73 /**
74 * Get the size of the remaining space in the current BO.
75 */
76 static uint32_t
77 tu_cs_get_space(const struct tu_cs *cs)
78 {
79 return cs->end - cs->cur;
80 }
81
82 /**
83 * Return true if there is no command packet emitted since the last call to
84 * tu_cs_add_entry.
85 */
86 static uint32_t
87 tu_cs_is_empty(const struct tu_cs *cs)
88 {
89 return tu_cs_get_size(cs) == 0;
90 }
91
92 /*
93 * Allocate and add a BO to a command stream. Following command packets will
94 * be emitted to the new BO.
95 */
96 static VkResult
97 tu_cs_add_bo(struct tu_device *dev, struct tu_cs *cs, uint32_t size)
98 {
99 /* grow cs->bos if needed */
100 if (cs->bo_count == cs->bo_capacity) {
101 uint32_t new_capacity = MAX2(4, 2 * cs->bo_capacity);
102 struct tu_bo **new_bos =
103 realloc(cs->bos, new_capacity * sizeof(struct tu_bo *));
104 if (!new_bos)
105 return VK_ERROR_OUT_OF_HOST_MEMORY;
106
107 cs->bo_capacity = new_capacity;
108 cs->bos = new_bos;
109 }
110
111 struct tu_bo *new_bo = malloc(sizeof(struct tu_bo));
112 if (!new_bo)
113 return VK_ERROR_OUT_OF_HOST_MEMORY;
114
115 VkResult result = tu_bo_init_new(dev, new_bo, size * sizeof(uint32_t));
116 if (result != VK_SUCCESS) {
117 free(new_bo);
118 return result;
119 }
120
121 result = tu_bo_map(dev, new_bo);
122 if (result != VK_SUCCESS) {
123 tu_bo_finish(dev, new_bo);
124 free(new_bo);
125 return result;
126 }
127
128 cs->bos[cs->bo_count++] = new_bo;
129
130 cs->start = cs->cur = (uint32_t *) new_bo->map;
131 cs->end = cs->start + new_bo->size / sizeof(uint32_t);
132
133 return VK_SUCCESS;
134 }
135
136 /**
137 * Reserve an IB entry.
138 */
139 static VkResult
140 tu_cs_reserve_entry(struct tu_cs *cs)
141 {
142 /* grow cs->entries if needed */
143 if (cs->entry_count == cs->entry_capacity) {
144 uint32_t new_capacity = MAX2(4, cs->entry_capacity * 2);
145 struct tu_cs_entry *new_entries =
146 realloc(cs->entries, new_capacity * sizeof(struct tu_cs_entry));
147 if (!new_entries)
148 return VK_ERROR_OUT_OF_HOST_MEMORY;
149
150 cs->entry_capacity = new_capacity;
151 cs->entries = new_entries;
152 }
153
154 return VK_SUCCESS;
155 }
156
157 /**
158 * Add an IB entry for the command packets emitted since the last call to this
159 * function.
160 */
161 static void
162 tu_cs_add_entry(struct tu_cs *cs)
163 {
164 /* disallow empty entry */
165 assert(!tu_cs_is_empty(cs));
166
167 /*
168 * because we disallow empty entry, tu_cs_add_bo and tu_cs_reserve_entry
169 * must both have been called
170 */
171 assert(cs->bo_count);
172 assert(cs->entry_count < cs->entry_capacity);
173
174 /* add an entry for [cs->start, cs->cur] */
175 cs->entries[cs->entry_count++] = (struct tu_cs_entry) {
176 .bo = cs->bos[cs->bo_count - 1],
177 .size = tu_cs_get_size(cs) * sizeof(uint32_t),
178 .offset = tu_cs_get_offset(cs) * sizeof(uint32_t),
179 };
180
181 cs->start = cs->cur;
182 }
183
184 /**
185 * Begin (or continue) command packet emission. This will reserve space from
186 * the command stream for at least \a reserve_size uint32_t values.
187 */
188 VkResult
189 tu_cs_begin(struct tu_device *dev, struct tu_cs *cs, uint32_t reserve_size)
190 {
191 /* no dangling command packet */
192 assert(tu_cs_is_empty(cs));
193
194 if (tu_cs_get_space(cs) < reserve_size) {
195 uint32_t new_size = MAX2(cs->next_bo_size, reserve_size);
196 VkResult result = tu_cs_add_bo(dev, cs, new_size);
197 if (result != VK_SUCCESS)
198 return result;
199
200 cs->next_bo_size = new_size * 2;
201 }
202
203 assert(tu_cs_get_space(cs) >= reserve_size);
204
205 return VK_SUCCESS;
206 }
207
208 /**
209 * End command packet emission by adding an IB entry for the command packets
210 * emitted since the last call to tu_cs_begin.
211 */
212 VkResult
213 tu_cs_end(struct tu_cs *cs)
214 {
215 /* no command packet at all */
216 if (tu_cs_is_empty(cs))
217 return VK_SUCCESS;
218
219 VkResult result = tu_cs_reserve_entry(cs);
220 if (result != VK_SUCCESS)
221 return result;
222
223 tu_cs_add_entry(cs);
224
225 return VK_SUCCESS;
226 }
227
228 /**
229 * Reset a command stream to its initial state. This discards all comand
230 * packets in \a cs, but does not necessarily release all resources.
231 */
232 void
233 tu_cs_reset(struct tu_device *dev, struct tu_cs *cs)
234 {
235 for (uint32_t i = 0; i + 1 < cs->bo_count; ++i) {
236 tu_bo_finish(dev, cs->bos[i]);
237 free(cs->bos[i]);
238 }
239
240 if (cs->bo_count) {
241 cs->bos[0] = cs->bos[cs->bo_count - 1];
242 cs->bo_count = 1;
243
244 cs->start = cs->cur = (uint32_t *) cs->bos[0]->map;
245 cs->end = cs->start + cs->bos[0]->size / sizeof(uint32_t);
246 }
247
248 cs->entry_count = 0;
249 }