2 * Copyright © 2019 Google LLC
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:
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
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.
27 * Initialize a command stream.
30 tu_cs_init(struct tu_cs
*cs
, enum tu_cs_mode mode
, uint32_t initial_size
)
32 assert(mode
!= TU_CS_MODE_EXTERNAL
);
34 memset(cs
, 0, sizeof(*cs
));
37 cs
->next_bo_size
= initial_size
;
41 * Initialize a command stream as a wrapper to an external buffer.
44 tu_cs_init_external(struct tu_cs
*cs
, uint32_t *start
, uint32_t *end
)
46 memset(cs
, 0, sizeof(*cs
));
48 cs
->mode
= TU_CS_MODE_EXTERNAL
;
49 cs
->start
= cs
->reserved_end
= cs
->cur
= start
;
54 * Finish and release all resources owned by a command stream.
57 tu_cs_finish(struct tu_device
*dev
, struct tu_cs
*cs
)
59 for (uint32_t i
= 0; i
< cs
->bo_count
; ++i
) {
60 tu_bo_finish(dev
, cs
->bos
[i
]);
69 * Get the offset of the command packets emitted since the last call to
73 tu_cs_get_offset(const struct tu_cs
*cs
)
76 return cs
->start
- (uint32_t *) cs
->bos
[cs
->bo_count
- 1]->map
;
80 * Get the size of the command packets emitted since the last call to
84 tu_cs_get_size(const struct tu_cs
*cs
)
86 return cs
->cur
- cs
->start
;
90 * Get the size of the remaining space in the current BO.
93 tu_cs_get_space(const struct tu_cs
*cs
)
95 return cs
->end
- cs
->cur
;
99 * Return true if there is no command packet emitted since the last call to
103 tu_cs_is_empty(const struct tu_cs
*cs
)
105 return tu_cs_get_size(cs
) == 0;
109 * Allocate and add a BO to a command stream. Following command packets will
110 * be emitted to the new BO.
113 tu_cs_add_bo(struct tu_device
*dev
, struct tu_cs
*cs
, uint32_t size
)
115 /* no BO for TU_CS_MODE_EXTERNAL */
116 assert(cs
->mode
!= TU_CS_MODE_EXTERNAL
);
118 /* no dangling command packet */
119 assert(tu_cs_is_empty(cs
));
121 /* grow cs->bos if needed */
122 if (cs
->bo_count
== cs
->bo_capacity
) {
123 uint32_t new_capacity
= MAX2(4, 2 * cs
->bo_capacity
);
124 struct tu_bo
**new_bos
=
125 realloc(cs
->bos
, new_capacity
* sizeof(struct tu_bo
*));
127 return VK_ERROR_OUT_OF_HOST_MEMORY
;
129 cs
->bo_capacity
= new_capacity
;
133 struct tu_bo
*new_bo
= malloc(sizeof(struct tu_bo
));
135 return VK_ERROR_OUT_OF_HOST_MEMORY
;
137 VkResult result
= tu_bo_init_new(dev
, new_bo
, size
* sizeof(uint32_t));
138 if (result
!= VK_SUCCESS
) {
143 result
= tu_bo_map(dev
, new_bo
);
144 if (result
!= VK_SUCCESS
) {
145 tu_bo_finish(dev
, new_bo
);
150 cs
->bos
[cs
->bo_count
++] = new_bo
;
152 cs
->start
= cs
->cur
= cs
->reserved_end
= (uint32_t *) new_bo
->map
;
153 cs
->end
= cs
->start
+ new_bo
->size
/ sizeof(uint32_t);
159 * Reserve an IB entry.
162 tu_cs_reserve_entry(struct tu_device
*dev
, struct tu_cs
*cs
)
164 /* entries are only for TU_CS_MODE_GROW */
165 assert(cs
->mode
== TU_CS_MODE_GROW
);
167 /* grow cs->entries if needed */
168 if (cs
->entry_count
== cs
->entry_capacity
) {
169 uint32_t new_capacity
= MAX2(4, cs
->entry_capacity
* 2);
170 struct tu_cs_entry
*new_entries
=
171 realloc(cs
->entries
, new_capacity
* sizeof(struct tu_cs_entry
));
173 return VK_ERROR_OUT_OF_HOST_MEMORY
;
175 cs
->entry_capacity
= new_capacity
;
176 cs
->entries
= new_entries
;
183 * Add an IB entry for the command packets emitted since the last call to this
187 tu_cs_add_entry(struct tu_cs
*cs
)
189 /* entries are only for TU_CS_MODE_GROW */
190 assert(cs
->mode
== TU_CS_MODE_GROW
);
192 /* disallow empty entry */
193 assert(!tu_cs_is_empty(cs
));
196 * because we disallow empty entry, tu_cs_add_bo and tu_cs_reserve_entry
197 * must both have been called
199 assert(cs
->bo_count
);
200 assert(cs
->entry_count
< cs
->entry_capacity
);
202 /* add an entry for [cs->start, cs->cur] */
203 cs
->entries
[cs
->entry_count
++] = (struct tu_cs_entry
) {
204 .bo
= cs
->bos
[cs
->bo_count
- 1],
205 .size
= tu_cs_get_size(cs
) * sizeof(uint32_t),
206 .offset
= tu_cs_get_offset(cs
) * sizeof(uint32_t),
213 * Begin (or continue) command packet emission. This does nothing but sanity
214 * checks currently. \a cs must not be in TU_CS_MODE_SUB_STREAM mode.
217 tu_cs_begin(struct tu_cs
*cs
)
219 assert(cs
->mode
!= TU_CS_MODE_SUB_STREAM
);
220 assert(tu_cs_is_empty(cs
));
224 * End command packet emission and add an IB entry.
227 tu_cs_end(struct tu_cs
*cs
)
229 assert(cs
->mode
!= TU_CS_MODE_SUB_STREAM
);
231 if (cs
->mode
== TU_CS_MODE_GROW
&& !tu_cs_is_empty(cs
))
236 * Begin command packet emission to a sub-stream. \a cs must be in
237 * TU_CS_MODE_SUB_STREAM mode.
239 * Return \a sub_cs which is in TU_CS_MODE_EXTERNAL mode.
242 tu_cs_begin_sub_stream(struct tu_device
*dev
,
245 struct tu_cs
*sub_cs
)
247 assert(cs
->mode
== TU_CS_MODE_SUB_STREAM
);
250 VkResult result
= tu_cs_reserve_space(dev
, cs
, size
);
251 if (result
!= VK_SUCCESS
)
254 tu_cs_init_external(sub_cs
, cs
->cur
, cs
->reserved_end
);
260 * End command packet emission to a sub-stream. \a sub_cs becomes invalid
263 * Return an IB entry for the sub-stream. The entry has the same lifetime as
267 tu_cs_end_sub_stream(struct tu_cs
*cs
, struct tu_cs
*sub_cs
)
269 assert(cs
->mode
== TU_CS_MODE_SUB_STREAM
);
270 assert(cs
->bo_count
);
271 assert(sub_cs
->cur
>= cs
->start
&& sub_cs
->cur
<= cs
->reserved_end
);
273 cs
->cur
= sub_cs
->cur
;
275 return (struct tu_cs_entry
) {
276 .bo
= cs
->bos
[cs
->bo_count
- 1],
277 .size
= tu_cs_get_size(cs
) * sizeof(uint32_t),
278 .offset
= tu_cs_get_offset(cs
) * sizeof(uint32_t),
283 * Reserve space from a command stream for \a reserved_size uint32_t values.
284 * This never fails when \a cs has mode TU_CS_MODE_EXTERNAL.
287 tu_cs_reserve_space(struct tu_device
*dev
,
289 uint32_t reserved_size
)
291 if (tu_cs_get_space(cs
) < reserved_size
) {
292 if (cs
->mode
== TU_CS_MODE_EXTERNAL
) {
293 unreachable("cannot grow external buffer");
294 return VK_ERROR_OUT_OF_HOST_MEMORY
;
297 /* add an entry for the exiting command packets */
298 if (!tu_cs_is_empty(cs
)) {
299 /* no direct command packet for TU_CS_MODE_SUB_STREAM */
300 assert(cs
->mode
!= TU_CS_MODE_SUB_STREAM
);
305 /* switch to a new BO */
306 uint32_t new_size
= MAX2(cs
->next_bo_size
, reserved_size
);
307 VkResult result
= tu_cs_add_bo(dev
, cs
, new_size
);
308 if (result
!= VK_SUCCESS
)
310 cs
->next_bo_size
= new_size
* 2;
313 assert(tu_cs_get_space(cs
) >= reserved_size
);
314 cs
->reserved_end
= cs
->cur
+ reserved_size
;
316 if (cs
->mode
== TU_CS_MODE_GROW
) {
317 /* reserve an entry for the next call to this function or tu_cs_end */
318 return tu_cs_reserve_entry(dev
, cs
);
325 * Reset a command stream to its initial state. This discards all comand
326 * packets in \a cs, but does not necessarily release all resources.
329 tu_cs_reset(struct tu_device
*dev
, struct tu_cs
*cs
)
331 if (cs
->mode
== TU_CS_MODE_EXTERNAL
) {
332 assert(!cs
->bo_count
&& !cs
->entry_count
);
333 cs
->reserved_end
= cs
->cur
= cs
->start
;
337 for (uint32_t i
= 0; i
+ 1 < cs
->bo_count
; ++i
) {
338 tu_bo_finish(dev
, cs
->bos
[i
]);
343 cs
->bos
[0] = cs
->bos
[cs
->bo_count
- 1];
346 cs
->start
= cs
->cur
= cs
->reserved_end
= (uint32_t *) cs
->bos
[0]->map
;
347 cs
->end
= cs
->start
+ cs
->bos
[0]->size
/ sizeof(uint32_t);