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_screen.h"
29 #include "virgl_encode.h"
30 #include "virgl_transfer_queue.h"
32 struct list_action_args
35 struct virgl_transfer
*queued
;
36 struct virgl_transfer
*current
;
39 typedef bool (*compare_transfers_t
)(struct virgl_transfer
*queued
,
40 struct virgl_transfer
*current
);
42 typedef void (*list_action_t
)(struct virgl_transfer_queue
*queue
,
43 struct list_action_args
*args
);
45 struct list_iteration_args
49 compare_transfers_t compare
;
50 struct virgl_transfer
*current
;
51 enum virgl_transfer_queue_lists type
;
54 static bool transfers_intersect(struct virgl_transfer
*queued
,
55 struct virgl_transfer
*current
)
58 struct pipe_resource
*queued_res
= queued
->base
.resource
;
59 struct pipe_resource
*current_res
= current
->base
.resource
;
61 if (queued_res
!= current_res
)
64 tmp
= u_box_test_intersection_2d(&queued
->base
.box
, ¤t
->base
.box
);
68 static bool transfers_overlap(struct virgl_transfer
*queued
,
69 struct virgl_transfer
*current
)
72 struct pipe_resource
*queued_res
= queued
->base
.resource
;
73 struct pipe_resource
*current_res
= current
->base
.resource
;
75 if (queued_res
!= current_res
)
78 if (queued
->base
.level
!= current
->base
.level
)
81 if (queued
->base
.box
.z
!= current
->base
.box
.z
)
84 if (queued
->base
.box
.depth
!= 1 || current
->base
.box
.depth
!= 1)
88 * Special case for boxes with [x: 0, width: 1] and [x: 1, width: 1].
90 if (queued_res
->target
== PIPE_BUFFER
) {
91 if (queued
->base
.box
.x
+ queued
->base
.box
.width
== current
->base
.box
.x
)
94 if (current
->base
.box
.x
+ current
->base
.box
.width
== queued
->base
.box
.x
)
98 tmp
= u_box_test_intersection_2d(&queued
->base
.box
, ¤t
->base
.box
);
102 static void set_true(UNUSED
struct virgl_transfer_queue
*queue
,
103 struct list_action_args
*args
)
105 bool *val
= args
->data
;
109 static void set_queued(UNUSED
struct virgl_transfer_queue
*queue
,
110 struct list_action_args
*args
)
112 struct virgl_transfer
*queued
= args
->queued
;
113 struct virgl_transfer
**val
= args
->data
;
117 static void remove_transfer(struct virgl_transfer_queue
*queue
,
118 struct list_action_args
*args
)
120 struct virgl_transfer
*queued
= args
->queued
;
121 struct pipe_resource
*pres
= queued
->base
.resource
;
122 list_del(&queued
->queue_link
);
123 pipe_resource_reference(&pres
, NULL
);
124 virgl_resource_destroy_transfer(queue
->pool
, queued
);
127 static void replace_unmapped_transfer(struct virgl_transfer_queue
*queue
,
128 struct list_action_args
*args
)
130 struct virgl_transfer
*current
= args
->current
;
131 struct virgl_transfer
*queued
= args
->queued
;
133 u_box_union_2d(¤t
->base
.box
, ¤t
->base
.box
, &queued
->base
.box
);
134 current
->offset
= current
->base
.box
.x
;
136 remove_transfer(queue
, args
);
137 queue
->num_dwords
-= (VIRGL_TRANSFER3D_SIZE
+ 1);
140 static void transfer_put(struct virgl_transfer_queue
*queue
,
141 struct list_action_args
*args
)
143 struct virgl_transfer
*queued
= args
->queued
;
144 struct virgl_resource
*res
= virgl_resource(queued
->base
.resource
);
146 queue
->vs
->vws
->transfer_put(queue
->vs
->vws
, res
->hw_res
, &queued
->base
.box
,
147 queued
->base
.stride
, queued
->l_stride
,
148 queued
->offset
, queued
->base
.level
);
150 remove_transfer(queue
, args
);
153 static void transfer_write(struct virgl_transfer_queue
*queue
,
154 struct list_action_args
*args
)
156 struct virgl_transfer
*queued
= args
->queued
;
157 struct virgl_cmd_buf
*buf
= args
->data
;
159 // Takes a reference on the HW resource, which is released after
160 // the exec buffer command.
161 virgl_encode_transfer(queue
->vs
, buf
, queued
, VIRGL_TRANSFER_TO_HOST
);
163 list_delinit(&queued
->queue_link
);
164 list_addtail(&queued
->queue_link
, &queue
->lists
[COMPLETED_LIST
]);
167 static void compare_and_perform_action(struct virgl_transfer_queue
*queue
,
168 struct list_iteration_args
*iter
)
170 struct list_action_args args
;
171 struct virgl_transfer
*queued
, *tmp
;
172 enum virgl_transfer_queue_lists type
= iter
->type
;
174 memset(&args
, 0, sizeof(args
));
175 args
.current
= iter
->current
;
176 args
.data
= iter
->data
;
178 LIST_FOR_EACH_ENTRY_SAFE(queued
, tmp
, &queue
->lists
[type
], queue_link
) {
179 if (iter
->compare(queued
, iter
->current
)) {
180 args
.queued
= queued
;
181 iter
->action(queue
, &args
);
186 static void intersect_and_set_queued_once(struct virgl_transfer_queue
*queue
,
187 struct list_iteration_args
*iter
)
189 struct list_action_args args
;
190 struct virgl_transfer
*queued
, *tmp
;
191 enum virgl_transfer_queue_lists type
= iter
->type
;
193 memset(&args
, 0, sizeof(args
));
194 args
.current
= iter
->current
;
195 args
.data
= iter
->data
;
197 LIST_FOR_EACH_ENTRY_SAFE(queued
, tmp
, &queue
->lists
[type
], queue_link
) {
198 if (transfers_intersect(queued
, iter
->current
)) {
199 args
.queued
= queued
;
200 set_queued(queue
, &args
);
206 static void perform_action(struct virgl_transfer_queue
*queue
,
207 struct list_iteration_args
*iter
)
209 struct list_action_args args
;
210 struct virgl_transfer
*queued
, *tmp
;
211 enum virgl_transfer_queue_lists type
= iter
->type
;
213 memset(&args
, 0, sizeof(args
));
214 args
.data
= iter
->data
;
216 LIST_FOR_EACH_ENTRY_SAFE(queued
, tmp
, &queue
->lists
[type
], queue_link
) {
217 args
.queued
= queued
;
218 iter
->action(queue
, &args
);
222 static void add_internal(struct virgl_transfer_queue
*queue
,
223 struct virgl_transfer
*transfer
)
225 uint32_t dwords
= VIRGL_TRANSFER3D_SIZE
+ 1;
227 if (queue
->num_dwords
+ dwords
>= VIRGL_MAX_TBUF_DWORDS
) {
228 struct list_iteration_args iter
;
229 struct virgl_winsys
*vws
= queue
->vs
->vws
;
231 memset(&iter
, 0, sizeof(iter
));
232 iter
.type
= PENDING_LIST
;
233 iter
.action
= transfer_write
;
234 iter
.data
= queue
->tbuf
;
235 perform_action(queue
, &iter
);
237 vws
->submit_cmd(vws
, queue
->tbuf
, NULL
);
238 queue
->num_dwords
= 0;
242 list_addtail(&transfer
->queue_link
, &queue
->lists
[PENDING_LIST
]);
243 queue
->num_dwords
+= dwords
;
247 void virgl_transfer_queue_init(struct virgl_transfer_queue
*queue
,
248 struct virgl_screen
*vs
,
249 struct slab_child_pool
*pool
)
253 queue
->num_dwords
= 0;
255 for (uint32_t i
= 0; i
< MAX_LISTS
; i
++)
256 list_inithead(&queue
->lists
[i
]);
258 if ((vs
->caps
.caps
.v2
.capability_bits
& VIRGL_CAP_TRANSFER
) &&
259 vs
->vws
->supports_encoded_transfers
)
260 queue
->tbuf
= vs
->vws
->cmd_buf_create(vs
->vws
, VIRGL_MAX_TBUF_DWORDS
);
265 void virgl_transfer_queue_fini(struct virgl_transfer_queue
*queue
)
267 struct virgl_winsys
*vws
= queue
->vs
->vws
;
268 struct list_iteration_args iter
;
270 memset(&iter
, 0, sizeof(iter
));
272 iter
.action
= transfer_put
;
273 iter
.type
= PENDING_LIST
;
274 perform_action(queue
, &iter
);
276 iter
.action
= remove_transfer
;
277 iter
.type
= COMPLETED_LIST
;
278 perform_action(queue
, &iter
);
281 vws
->cmd_buf_destroy(queue
->tbuf
);
286 queue
->num_dwords
= 0;
289 int virgl_transfer_queue_unmap(struct virgl_transfer_queue
*queue
,
290 struct virgl_transfer
*transfer
)
292 struct pipe_resource
*res
, *pres
;
293 struct list_iteration_args iter
;
296 res
= transfer
->base
.resource
;
297 pipe_resource_reference(&pres
, res
);
299 if (res
->target
== PIPE_BUFFER
) {
300 memset(&iter
, 0, sizeof(iter
));
301 iter
.current
= transfer
;
302 iter
.compare
= transfers_intersect
;
303 iter
.action
= replace_unmapped_transfer
;
304 iter
.type
= PENDING_LIST
;
305 compare_and_perform_action(queue
, &iter
);
308 add_internal(queue
, transfer
);
312 int virgl_transfer_queue_clear(struct virgl_transfer_queue
*queue
,
313 struct virgl_cmd_buf
*cbuf
)
315 struct list_iteration_args iter
;
317 memset(&iter
, 0, sizeof(iter
));
318 iter
.type
= PENDING_LIST
;
320 uint32_t prior_num_dwords
= cbuf
->cdw
;
323 iter
.action
= transfer_write
;
325 perform_action(queue
, &iter
);
327 virgl_encode_end_transfers(cbuf
);
328 cbuf
->cdw
= prior_num_dwords
;
330 iter
.action
= transfer_put
;
331 perform_action(queue
, &iter
);
334 iter
.action
= remove_transfer
;
335 iter
.type
= COMPLETED_LIST
;
336 perform_action(queue
, &iter
);
337 queue
->num_dwords
= 0;
342 bool virgl_transfer_queue_is_queued(struct virgl_transfer_queue
*queue
,
343 struct virgl_transfer
*transfer
)
346 struct list_iteration_args iter
;
348 memset(&iter
, 0, sizeof(iter
));
349 iter
.current
= transfer
;
350 iter
.compare
= transfers_overlap
;
351 iter
.action
= set_true
;
354 iter
.type
= PENDING_LIST
;
355 compare_and_perform_action(queue
, &iter
);
357 iter
.type
= COMPLETED_LIST
;
358 compare_and_perform_action(queue
, &iter
);
363 struct virgl_transfer
*
364 virgl_transfer_queue_extend(struct virgl_transfer_queue
*queue
,
365 struct virgl_transfer
*transfer
)
367 struct virgl_transfer
*queued
= NULL
;
368 struct list_iteration_args iter
;
370 if (transfer
->base
.resource
->target
== PIPE_BUFFER
) {
371 memset(&iter
, 0, sizeof(iter
));
372 iter
.current
= transfer
;
374 iter
.type
= PENDING_LIST
;
375 intersect_and_set_queued_once(queue
, &iter
);
379 u_box_union_2d(&queued
->base
.box
, &queued
->base
.box
, &transfer
->base
.box
);
380 queued
->offset
= queued
->base
.box
.x
;