2 * Copyright 2018 Chromium.
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 * on the rights to use, copy, modify, merge, publish, distribute, sub
8 * license, and/or sell copies of the Software, and to permit persons to whom
9 * the 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 NON-INFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
19 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
20 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
21 * USE OR OTHER DEALINGS IN THE SOFTWARE.
24 #include "util/u_box.h"
25 #include "util/u_inlines.h"
27 #include "virgl_protocol.h"
28 #include "virgl_context.h"
29 #include "virgl_screen.h"
30 #include "virgl_encode.h"
31 #include "virgl_resource.h"
32 #include "virgl_transfer_queue.h"
34 struct list_action_args
37 struct virgl_transfer
*queued
;
38 struct virgl_transfer
*current
;
41 typedef bool (*compare_transfers_t
)(struct virgl_transfer
*queued
,
42 struct virgl_transfer
*current
);
44 typedef void (*list_action_t
)(struct virgl_transfer_queue
*queue
,
45 struct list_action_args
*args
);
47 struct list_iteration_args
51 compare_transfers_t compare
;
52 struct virgl_transfer
*current
;
53 enum virgl_transfer_queue_lists type
;
56 static bool transfers_intersect(struct virgl_transfer
*queued
,
57 struct virgl_transfer
*current
)
60 struct pipe_resource
*queued_res
= queued
->base
.resource
;
61 struct pipe_resource
*current_res
= current
->base
.resource
;
63 if (queued_res
!= current_res
)
66 tmp
= u_box_test_intersection_2d(&queued
->base
.box
, ¤t
->base
.box
);
70 static bool transfers_overlap(struct virgl_transfer
*queued
,
71 struct virgl_transfer
*current
)
74 struct pipe_resource
*queued_res
= queued
->base
.resource
;
75 struct pipe_resource
*current_res
= current
->base
.resource
;
77 if (queued_res
!= current_res
)
80 if (queued
->base
.level
!= current
->base
.level
)
83 if (queued
->base
.box
.z
!= current
->base
.box
.z
)
86 if (queued
->base
.box
.depth
!= 1 || current
->base
.box
.depth
!= 1)
90 * Special case for boxes with [x: 0, width: 1] and [x: 1, width: 1].
92 if (queued_res
->target
== PIPE_BUFFER
) {
93 if (queued
->base
.box
.x
+ queued
->base
.box
.width
== current
->base
.box
.x
)
96 if (current
->base
.box
.x
+ current
->base
.box
.width
== queued
->base
.box
.x
)
100 tmp
= u_box_test_intersection_2d(&queued
->base
.box
, ¤t
->base
.box
);
101 return (tmp
== TRUE
);
104 static void set_true(UNUSED
struct virgl_transfer_queue
*queue
,
105 struct list_action_args
*args
)
107 bool *val
= args
->data
;
111 static void set_queued(UNUSED
struct virgl_transfer_queue
*queue
,
112 struct list_action_args
*args
)
114 struct virgl_transfer
*queued
= args
->queued
;
115 struct virgl_transfer
**val
= args
->data
;
119 static void remove_transfer(struct virgl_transfer_queue
*queue
,
120 struct list_action_args
*args
)
122 struct virgl_transfer
*queued
= args
->queued
;
123 struct pipe_resource
*pres
= queued
->base
.resource
;
124 list_del(&queued
->queue_link
);
125 pipe_resource_reference(&pres
, NULL
);
126 virgl_resource_destroy_transfer(&queue
->vctx
->transfer_pool
, queued
);
129 static void replace_unmapped_transfer(struct virgl_transfer_queue
*queue
,
130 struct list_action_args
*args
)
132 struct virgl_transfer
*current
= args
->current
;
133 struct virgl_transfer
*queued
= args
->queued
;
135 u_box_union_2d(¤t
->base
.box
, ¤t
->base
.box
, &queued
->base
.box
);
136 current
->offset
= current
->base
.box
.x
;
138 remove_transfer(queue
, args
);
139 queue
->num_dwords
-= (VIRGL_TRANSFER3D_SIZE
+ 1);
142 static void transfer_put(struct virgl_transfer_queue
*queue
,
143 struct list_action_args
*args
)
145 struct virgl_transfer
*queued
= args
->queued
;
146 struct virgl_resource
*res
= virgl_resource(queued
->base
.resource
);
148 queue
->vs
->vws
->transfer_put(queue
->vs
->vws
, res
->hw_res
, &queued
->base
.box
,
149 queued
->base
.stride
, queued
->l_stride
,
150 queued
->offset
, queued
->base
.level
);
152 remove_transfer(queue
, args
);
155 static void transfer_write(struct virgl_transfer_queue
*queue
,
156 struct list_action_args
*args
)
158 struct virgl_transfer
*queued
= args
->queued
;
159 struct virgl_cmd_buf
*buf
= args
->data
;
161 // Takes a reference on the HW resource, which is released after
162 // the exec buffer command.
163 virgl_encode_transfer(queue
->vs
, buf
, queued
, VIRGL_TRANSFER_TO_HOST
);
165 list_delinit(&queued
->queue_link
);
166 list_addtail(&queued
->queue_link
, &queue
->lists
[COMPLETED_LIST
]);
169 static void compare_and_perform_action(struct virgl_transfer_queue
*queue
,
170 struct list_iteration_args
*iter
)
172 struct list_action_args args
;
173 struct virgl_transfer
*queued
, *tmp
;
174 enum virgl_transfer_queue_lists type
= iter
->type
;
176 memset(&args
, 0, sizeof(args
));
177 args
.current
= iter
->current
;
178 args
.data
= iter
->data
;
180 LIST_FOR_EACH_ENTRY_SAFE(queued
, tmp
, &queue
->lists
[type
], queue_link
) {
181 if (iter
->compare(queued
, iter
->current
)) {
182 args
.queued
= queued
;
183 iter
->action(queue
, &args
);
188 static void intersect_and_set_queued_once(struct virgl_transfer_queue
*queue
,
189 struct list_iteration_args
*iter
)
191 struct list_action_args args
;
192 struct virgl_transfer
*queued
, *tmp
;
193 enum virgl_transfer_queue_lists type
= iter
->type
;
195 memset(&args
, 0, sizeof(args
));
196 args
.current
= iter
->current
;
197 args
.data
= iter
->data
;
199 LIST_FOR_EACH_ENTRY_SAFE(queued
, tmp
, &queue
->lists
[type
], queue_link
) {
200 if (transfers_intersect(queued
, iter
->current
)) {
201 args
.queued
= queued
;
202 set_queued(queue
, &args
);
208 static void perform_action(struct virgl_transfer_queue
*queue
,
209 struct list_iteration_args
*iter
)
211 struct list_action_args args
;
212 struct virgl_transfer
*queued
, *tmp
;
213 enum virgl_transfer_queue_lists type
= iter
->type
;
215 memset(&args
, 0, sizeof(args
));
216 args
.data
= iter
->data
;
218 LIST_FOR_EACH_ENTRY_SAFE(queued
, tmp
, &queue
->lists
[type
], queue_link
) {
219 args
.queued
= queued
;
220 iter
->action(queue
, &args
);
224 static void add_internal(struct virgl_transfer_queue
*queue
,
225 struct virgl_transfer
*transfer
)
227 uint32_t dwords
= VIRGL_TRANSFER3D_SIZE
+ 1;
229 if (queue
->num_dwords
+ dwords
>= VIRGL_MAX_TBUF_DWORDS
) {
230 struct list_iteration_args iter
;
231 struct virgl_winsys
*vws
= queue
->vs
->vws
;
233 memset(&iter
, 0, sizeof(iter
));
234 iter
.type
= PENDING_LIST
;
235 iter
.action
= transfer_write
;
236 iter
.data
= queue
->tbuf
;
237 perform_action(queue
, &iter
);
239 vws
->submit_cmd(vws
, queue
->tbuf
, NULL
);
240 queue
->num_dwords
= 0;
244 list_addtail(&transfer
->queue_link
, &queue
->lists
[PENDING_LIST
]);
245 queue
->num_dwords
+= dwords
;
249 void virgl_transfer_queue_init(struct virgl_transfer_queue
*queue
,
250 struct virgl_context
*vctx
)
252 struct virgl_screen
*vs
= virgl_screen(vctx
->base
.screen
);
256 queue
->num_dwords
= 0;
258 for (uint32_t i
= 0; i
< MAX_LISTS
; i
++)
259 list_inithead(&queue
->lists
[i
]);
261 if ((vs
->caps
.caps
.v2
.capability_bits
& VIRGL_CAP_TRANSFER
) &&
262 vs
->vws
->supports_encoded_transfers
)
263 queue
->tbuf
= vs
->vws
->cmd_buf_create(vs
->vws
, VIRGL_MAX_TBUF_DWORDS
);
268 void virgl_transfer_queue_fini(struct virgl_transfer_queue
*queue
)
270 struct virgl_winsys
*vws
= queue
->vs
->vws
;
271 struct list_iteration_args iter
;
273 memset(&iter
, 0, sizeof(iter
));
275 iter
.action
= transfer_put
;
276 iter
.type
= PENDING_LIST
;
277 perform_action(queue
, &iter
);
279 iter
.action
= remove_transfer
;
280 iter
.type
= COMPLETED_LIST
;
281 perform_action(queue
, &iter
);
284 vws
->cmd_buf_destroy(queue
->tbuf
);
289 queue
->num_dwords
= 0;
292 int virgl_transfer_queue_unmap(struct virgl_transfer_queue
*queue
,
293 struct virgl_transfer
*transfer
)
295 struct pipe_resource
*res
, *pres
;
296 struct list_iteration_args iter
;
299 res
= transfer
->base
.resource
;
300 pipe_resource_reference(&pres
, res
);
302 /* We don't support copy transfers in the transfer queue. */
303 assert(!transfer
->copy_src_res
);
305 /* Attempt to merge multiple intersecting transfers into a single one. */
306 if (res
->target
== PIPE_BUFFER
) {
307 memset(&iter
, 0, sizeof(iter
));
308 iter
.current
= transfer
;
309 iter
.compare
= transfers_intersect
;
310 iter
.action
= replace_unmapped_transfer
;
311 iter
.type
= PENDING_LIST
;
312 compare_and_perform_action(queue
, &iter
);
315 add_internal(queue
, transfer
);
319 int virgl_transfer_queue_clear(struct virgl_transfer_queue
*queue
,
320 struct virgl_cmd_buf
*cbuf
)
322 struct list_iteration_args iter
;
324 memset(&iter
, 0, sizeof(iter
));
325 iter
.type
= PENDING_LIST
;
327 uint32_t prior_num_dwords
= cbuf
->cdw
;
330 iter
.action
= transfer_write
;
332 perform_action(queue
, &iter
);
334 virgl_encode_end_transfers(cbuf
);
335 cbuf
->cdw
= prior_num_dwords
;
337 iter
.action
= transfer_put
;
338 perform_action(queue
, &iter
);
341 iter
.action
= remove_transfer
;
342 iter
.type
= COMPLETED_LIST
;
343 perform_action(queue
, &iter
);
344 queue
->num_dwords
= 0;
349 bool virgl_transfer_queue_is_queued(struct virgl_transfer_queue
*queue
,
350 struct virgl_transfer
*transfer
)
353 struct list_iteration_args iter
;
355 memset(&iter
, 0, sizeof(iter
));
356 iter
.current
= transfer
;
357 iter
.compare
= transfers_overlap
;
358 iter
.action
= set_true
;
361 iter
.type
= PENDING_LIST
;
362 compare_and_perform_action(queue
, &iter
);
364 iter
.type
= COMPLETED_LIST
;
365 compare_and_perform_action(queue
, &iter
);
370 struct virgl_transfer
*
371 virgl_transfer_queue_extend(struct virgl_transfer_queue
*queue
,
372 struct virgl_transfer
*transfer
)
374 struct virgl_transfer
*queued
= NULL
;
375 struct list_iteration_args iter
;
377 /* We don't support extending from copy transfers. */
378 assert(!transfer
->copy_src_res
);
380 if (transfer
->base
.resource
->target
== PIPE_BUFFER
) {
381 memset(&iter
, 0, sizeof(iter
));
382 iter
.current
= transfer
;
384 iter
.type
= PENDING_LIST
;
385 intersect_and_set_queued_once(queue
, &iter
);
389 u_box_union_2d(&queued
->base
.box
, &queued
->base
.box
, &transfer
->base
.box
);
390 queued
->offset
= queued
->base
.box
.x
;