Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mesa
Path: blob/21.2-virgl/src/freedreno/vulkan/tu_cs.h
4565 views
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
#ifndef TU_CS_H
24
#define TU_CS_H
25
26
#include "tu_private.h"
27
28
#include "adreno_pm4.xml.h"
29
30
#include "freedreno_pm4.h"
31
32
void
33
tu_cs_init(struct tu_cs *cs,
34
struct tu_device *device,
35
enum tu_cs_mode mode,
36
uint32_t initial_size);
37
38
void
39
tu_cs_init_external(struct tu_cs *cs, uint32_t *start, uint32_t *end);
40
41
void
42
tu_cs_finish(struct tu_cs *cs);
43
44
void
45
tu_cs_begin(struct tu_cs *cs);
46
47
void
48
tu_cs_end(struct tu_cs *cs);
49
50
VkResult
51
tu_cs_begin_sub_stream(struct tu_cs *cs, uint32_t size, struct tu_cs *sub_cs);
52
53
VkResult
54
tu_cs_alloc(struct tu_cs *cs,
55
uint32_t count,
56
uint32_t size,
57
struct tu_cs_memory *memory);
58
59
struct tu_cs_entry
60
tu_cs_end_sub_stream(struct tu_cs *cs, struct tu_cs *sub_cs);
61
62
static inline struct tu_draw_state
63
tu_cs_end_draw_state(struct tu_cs *cs, struct tu_cs *sub_cs)
64
{
65
struct tu_cs_entry entry = tu_cs_end_sub_stream(cs, sub_cs);
66
return (struct tu_draw_state) {
67
.iova = entry.bo->iova + entry.offset,
68
.size = entry.size / sizeof(uint32_t),
69
};
70
}
71
72
VkResult
73
tu_cs_reserve_space(struct tu_cs *cs, uint32_t reserved_size);
74
75
static inline struct tu_draw_state
76
tu_cs_draw_state(struct tu_cs *sub_cs, struct tu_cs *cs, uint32_t size)
77
{
78
struct tu_cs_memory memory;
79
80
/* TODO: clean this up */
81
tu_cs_alloc(sub_cs, size, 1, &memory);
82
tu_cs_init_external(cs, memory.map, memory.map + size);
83
tu_cs_begin(cs);
84
tu_cs_reserve_space(cs, size);
85
86
return (struct tu_draw_state) {
87
.iova = memory.iova,
88
.size = size,
89
};
90
}
91
92
void
93
tu_cs_reset(struct tu_cs *cs);
94
95
VkResult
96
tu_cs_add_entries(struct tu_cs *cs, struct tu_cs *target);
97
98
/**
99
* Get the size of the command packets emitted since the last call to
100
* tu_cs_add_entry.
101
*/
102
static inline uint32_t
103
tu_cs_get_size(const struct tu_cs *cs)
104
{
105
return cs->cur - cs->start;
106
}
107
108
/**
109
* Return true if there is no command packet emitted since the last call to
110
* tu_cs_add_entry.
111
*/
112
static inline uint32_t
113
tu_cs_is_empty(const struct tu_cs *cs)
114
{
115
return tu_cs_get_size(cs) == 0;
116
}
117
118
/**
119
* Discard all entries. This allows \a cs to be reused while keeping the
120
* existing BOs and command packets intact.
121
*/
122
static inline void
123
tu_cs_discard_entries(struct tu_cs *cs)
124
{
125
assert(cs->mode == TU_CS_MODE_GROW);
126
cs->entry_count = 0;
127
}
128
129
/**
130
* Get the size needed for tu_cs_emit_call.
131
*/
132
static inline uint32_t
133
tu_cs_get_call_size(const struct tu_cs *cs)
134
{
135
assert(cs->mode == TU_CS_MODE_GROW);
136
/* each CP_INDIRECT_BUFFER needs 4 dwords */
137
return cs->entry_count * 4;
138
}
139
140
/**
141
* Assert that we did not exceed the reserved space.
142
*/
143
static inline void
144
tu_cs_sanity_check(const struct tu_cs *cs)
145
{
146
assert(cs->start <= cs->cur);
147
assert(cs->cur <= cs->reserved_end);
148
assert(cs->reserved_end <= cs->end);
149
}
150
151
/**
152
* Emit a uint32_t value into a command stream, without boundary checking.
153
*/
154
static inline void
155
tu_cs_emit(struct tu_cs *cs, uint32_t value)
156
{
157
assert(cs->cur < cs->reserved_end);
158
*cs->cur = value;
159
++cs->cur;
160
}
161
162
/**
163
* Emit an array of uint32_t into a command stream, without boundary checking.
164
*/
165
static inline void
166
tu_cs_emit_array(struct tu_cs *cs, const uint32_t *values, uint32_t length)
167
{
168
assert(cs->cur + length <= cs->reserved_end);
169
memcpy(cs->cur, values, sizeof(uint32_t) * length);
170
cs->cur += length;
171
}
172
173
/**
174
* Get the size of the remaining space in the current BO.
175
*/
176
static inline uint32_t
177
tu_cs_get_space(const struct tu_cs *cs)
178
{
179
return cs->end - cs->cur;
180
}
181
182
static inline void
183
tu_cs_reserve(struct tu_cs *cs, uint32_t reserved_size)
184
{
185
if (cs->mode != TU_CS_MODE_GROW) {
186
assert(tu_cs_get_space(cs) >= reserved_size);
187
assert(cs->reserved_end == cs->end);
188
return;
189
}
190
191
if (tu_cs_get_space(cs) >= reserved_size &&
192
cs->entry_count < cs->entry_capacity) {
193
cs->reserved_end = cs->cur + reserved_size;
194
return;
195
}
196
197
ASSERTED VkResult result = tu_cs_reserve_space(cs, reserved_size);
198
/* TODO: set this error in tu_cs and use it */
199
assert(result == VK_SUCCESS);
200
}
201
202
/**
203
* Emit a type-4 command packet header into a command stream.
204
*/
205
static inline void
206
tu_cs_emit_pkt4(struct tu_cs *cs, uint16_t regindx, uint16_t cnt)
207
{
208
tu_cs_reserve(cs, cnt + 1);
209
tu_cs_emit(cs, pm4_pkt4_hdr(regindx, cnt));
210
}
211
212
/**
213
* Emit a type-7 command packet header into a command stream.
214
*/
215
static inline void
216
tu_cs_emit_pkt7(struct tu_cs *cs, uint8_t opcode, uint16_t cnt)
217
{
218
tu_cs_reserve(cs, cnt + 1);
219
tu_cs_emit(cs, pm4_pkt7_hdr(opcode, cnt));
220
}
221
222
static inline void
223
tu_cs_emit_wfi(struct tu_cs *cs)
224
{
225
tu_cs_emit_pkt7(cs, CP_WAIT_FOR_IDLE, 0);
226
}
227
228
static inline void
229
tu_cs_emit_qw(struct tu_cs *cs, uint64_t value)
230
{
231
tu_cs_emit(cs, (uint32_t) value);
232
tu_cs_emit(cs, (uint32_t) (value >> 32));
233
}
234
235
static inline void
236
tu_cs_emit_write_reg(struct tu_cs *cs, uint16_t reg, uint32_t value)
237
{
238
tu_cs_emit_pkt4(cs, reg, 1);
239
tu_cs_emit(cs, value);
240
}
241
242
/**
243
* Emit a CP_INDIRECT_BUFFER command packet.
244
*/
245
static inline void
246
tu_cs_emit_ib(struct tu_cs *cs, const struct tu_cs_entry *entry)
247
{
248
assert(entry->bo);
249
assert(entry->size && entry->offset + entry->size <= entry->bo->size);
250
assert(entry->size % sizeof(uint32_t) == 0);
251
assert(entry->offset % sizeof(uint32_t) == 0);
252
253
tu_cs_emit_pkt7(cs, CP_INDIRECT_BUFFER, 3);
254
tu_cs_emit_qw(cs, entry->bo->iova + entry->offset);
255
tu_cs_emit(cs, entry->size / sizeof(uint32_t));
256
}
257
258
/* for compute which isn't using SET_DRAW_STATE */
259
static inline void
260
tu_cs_emit_state_ib(struct tu_cs *cs, struct tu_draw_state state)
261
{
262
if (state.size) {
263
tu_cs_emit_pkt7(cs, CP_INDIRECT_BUFFER, 3);
264
tu_cs_emit_qw(cs, state.iova);
265
tu_cs_emit(cs, state.size);
266
}
267
}
268
269
/**
270
* Emit a CP_INDIRECT_BUFFER command packet for each entry in the target
271
* command stream.
272
*/
273
static inline void
274
tu_cs_emit_call(struct tu_cs *cs, const struct tu_cs *target)
275
{
276
assert(target->mode == TU_CS_MODE_GROW);
277
for (uint32_t i = 0; i < target->entry_count; i++)
278
tu_cs_emit_ib(cs, target->entries + i);
279
}
280
281
/* Helpers for bracketing a large sequence of commands of unknown size inside
282
* a CP_COND_REG_EXEC packet.
283
*/
284
static inline void
285
tu_cond_exec_start(struct tu_cs *cs, uint32_t cond_flags)
286
{
287
assert(cs->mode == TU_CS_MODE_GROW);
288
assert(!cs->cond_flags && cond_flags);
289
290
tu_cs_emit_pkt7(cs, CP_COND_REG_EXEC, 2);
291
tu_cs_emit(cs, cond_flags);
292
293
cs->cond_flags = cond_flags;
294
cs->cond_dwords = cs->cur;
295
296
/* Emit dummy DWORD field here */
297
tu_cs_emit(cs, CP_COND_REG_EXEC_1_DWORDS(0));
298
}
299
#define CP_COND_EXEC_0_RENDER_MODE_GMEM \
300
(CP_COND_REG_EXEC_0_MODE(RENDER_MODE) | CP_COND_REG_EXEC_0_GMEM)
301
#define CP_COND_EXEC_0_RENDER_MODE_SYSMEM \
302
(CP_COND_REG_EXEC_0_MODE(RENDER_MODE) | CP_COND_REG_EXEC_0_SYSMEM)
303
304
static inline void
305
tu_cond_exec_end(struct tu_cs *cs)
306
{
307
assert(cs->cond_flags);
308
309
cs->cond_flags = 0;
310
/* Subtract one here to account for the DWORD field itself. */
311
*cs->cond_dwords = cs->cur - cs->cond_dwords - 1;
312
}
313
314
#define fd_reg_pair tu_reg_value
315
#define __bo_type struct tu_bo *
316
317
#include "a6xx.xml.h"
318
#include "a6xx-pack.xml.h"
319
320
#define __assert_eq(a, b) \
321
do { \
322
if ((a) != (b)) { \
323
fprintf(stderr, "assert failed: " #a " (0x%x) != " #b " (0x%x)\n", a, b); \
324
assert((a) == (b)); \
325
} \
326
} while (0)
327
328
#define __ONE_REG(i, regs) \
329
do { \
330
if (i < ARRAY_SIZE(regs) && regs[i].reg > 0) { \
331
__assert_eq(regs[0].reg + i, regs[i].reg); \
332
if (regs[i].bo) { \
333
uint64_t v = regs[i].bo->iova + regs[i].bo_offset; \
334
v >>= regs[i].bo_shift; \
335
v |= regs[i].value; \
336
\
337
*p++ = v; \
338
*p++ = v >> 32; \
339
} else { \
340
*p++ = regs[i].value; \
341
if (regs[i].is_address) \
342
*p++ = regs[i].value >> 32; \
343
} \
344
} \
345
} while (0)
346
347
/* Emits a sequence of register writes in order using a pkt4. This will check
348
* (at runtime on a !NDEBUG build) that the registers were actually set up in
349
* order in the code.
350
*
351
* Note that references to buffers aren't automatically added to the CS,
352
* unlike in freedreno. We are clever in various places to avoid duplicating
353
* the reference add work.
354
*
355
* Also, 64-bit address registers don't have a way (currently) to set a 64-bit
356
* address without having a reference to a BO, since the .dword field in the
357
* register's struct is only 32-bit wide. We should fix this in the pack
358
* codegen later.
359
*/
360
#define tu_cs_emit_regs(cs, ...) do { \
361
const struct fd_reg_pair regs[] = { __VA_ARGS__ }; \
362
unsigned count = ARRAY_SIZE(regs); \
363
\
364
STATIC_ASSERT(count > 0); \
365
STATIC_ASSERT(count <= 16); \
366
\
367
tu_cs_emit_pkt4((cs), regs[0].reg, count); \
368
uint32_t *p = (cs)->cur; \
369
__ONE_REG( 0, regs); \
370
__ONE_REG( 1, regs); \
371
__ONE_REG( 2, regs); \
372
__ONE_REG( 3, regs); \
373
__ONE_REG( 4, regs); \
374
__ONE_REG( 5, regs); \
375
__ONE_REG( 6, regs); \
376
__ONE_REG( 7, regs); \
377
__ONE_REG( 8, regs); \
378
__ONE_REG( 9, regs); \
379
__ONE_REG(10, regs); \
380
__ONE_REG(11, regs); \
381
__ONE_REG(12, regs); \
382
__ONE_REG(13, regs); \
383
__ONE_REG(14, regs); \
384
__ONE_REG(15, regs); \
385
(cs)->cur = p; \
386
} while (0)
387
388
#endif /* TU_CS_H */
389
390