turnip: document tu_cs
[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)
31 {
32 cs->start = cs->cur = cs->end = NULL;
33
34 cs->entry_count = cs->entry_capacity = 0;
35 cs->entries = NULL;
36
37 cs->bo_count = cs->bo_capacity = 0;
38 cs->bos = NULL;
39 }
40
41 /**
42 * Finish and release all resources owned by a command stream.
43 */
44 void
45 tu_cs_finish(struct tu_device *dev, struct tu_cs *cs)
46 {
47 for (uint32_t i = 0; i < cs->bo_count; ++i) {
48 tu_bo_finish(dev, cs->bos[i]);
49 free(cs->bos[i]);
50 }
51
52 free(cs->entries);
53 free(cs->bos);
54 }
55
56 /**
57 * Begin (or continue) command packet emission. This will reserve space from
58 * the command stream for at least \a reserve_size uint32_t values.
59 */
60 VkResult
61 tu_cs_begin(struct tu_device *dev, struct tu_cs *cs, uint32_t reserve_size)
62 {
63 assert(reserve_size);
64
65 if (cs->end - cs->cur < reserve_size) {
66 if (cs->bo_count == cs->bo_capacity) {
67 uint32_t new_capacity = MAX2(4, 2 * cs->bo_capacity);
68 struct tu_bo **new_bos =
69 realloc(cs->bos, new_capacity * sizeof(struct tu_bo *));
70 if (!new_bos)
71 abort();
72
73 cs->bo_capacity = new_capacity;
74 cs->bos = new_bos;
75 }
76
77 uint32_t new_size = MAX2(16384, reserve_size * sizeof(uint32_t));
78 if (cs->bo_count)
79 new_size = MAX2(new_size, cs->bos[cs->bo_count - 1]->size * 2);
80
81 struct tu_bo *new_bo = malloc(sizeof(struct tu_bo));
82 if (!new_bo)
83 abort();
84
85 VkResult result = tu_bo_init_new(dev, new_bo, new_size);
86 if (result != VK_SUCCESS) {
87 free(new_bo);
88 return result;
89 }
90
91 result = tu_bo_map(dev, new_bo);
92 if (result != VK_SUCCESS) {
93 tu_bo_finish(dev, new_bo);
94 free(new_bo);
95 return result;
96 }
97
98 cs->bos[cs->bo_count] = new_bo;
99 ++cs->bo_count;
100
101 cs->start = cs->cur = (uint32_t *) new_bo->map;
102 cs->end = cs->start + new_bo->size / sizeof(uint32_t);
103 }
104 cs->start = cs->cur;
105
106 return VK_SUCCESS;
107 }
108
109 /**
110 * End command packet emission by adding an IB entry for the command packets
111 * emitted since the last call to tu_cs_begin.
112 */
113 VkResult
114 tu_cs_end(struct tu_cs *cs)
115 {
116 if (cs->start == cs->cur)
117 return VK_SUCCESS;
118
119 if (cs->entry_capacity == cs->entry_count) {
120 uint32_t new_capacity = MAX2(cs->entry_capacity * 2, 4);
121 struct tu_cs_entry *new_entries =
122 realloc(cs->entries, new_capacity * sizeof(struct tu_cs_entry));
123 if (!new_entries)
124 abort(); /* TODO */
125
126 cs->entries = new_entries;
127 cs->entry_capacity = new_capacity;
128 }
129
130 assert(cs->bo_count);
131
132 struct tu_cs_entry entry;
133 entry.bo = cs->bos[cs->bo_count - 1];
134 entry.size = (cs->cur - cs->start) * sizeof(uint32_t);
135 entry.offset = (cs->start - (uint32_t *) entry.bo->map) * sizeof(uint32_t);
136
137 cs->entries[cs->entry_count] = entry;
138 ++cs->entry_count;
139
140 return VK_SUCCESS;
141 }
142
143 /**
144 * Reset a command stream to its initial state. This discards all comand
145 * packets in \a cs, but does not necessarily release all resources.
146 */
147 void
148 tu_cs_reset(struct tu_device *dev, struct tu_cs *cs)
149 {
150 for (uint32_t i = 0; i + 1 < cs->bo_count; ++i) {
151 tu_bo_finish(dev, cs->bos[i]);
152 free(cs->bos[i]);
153 }
154
155 if (cs->bo_count) {
156 cs->bos[0] = cs->bos[cs->bo_count - 1];
157 cs->bo_count = 1;
158
159 cs->start = cs->cur = (uint32_t *) cs->bos[0]->map;
160 cs->end = cs->start + cs->bos[0]->size / sizeof(uint32_t);
161 }
162
163 cs->entry_count = 0;
164 }
165
166 /**
167 * Reserve space from a command stream for \a size uint32_t values.
168 */
169 VkResult
170 tu_cs_check_space(struct tu_device *dev, struct tu_cs *cs, size_t size)
171 {
172 if (cs->end - cs->cur >= size)
173 return VK_SUCCESS;
174
175 VkResult result = tu_cs_end(cs);
176 if (result != VK_SUCCESS)
177 return result;
178
179 return tu_cs_begin(dev, cs, size);
180 }