2 * Copyright 2014, 2015 Red Hat.
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_memory.h"
25 #include "util/u_format.h"
26 #include "util/u_inlines.h"
27 #include "os/os_time.h"
28 #include "state_tracker/sw_winsys.h"
30 #include "virgl_vtest_winsys.h"
31 #include "virgl_vtest_public.h"
33 static void *virgl_vtest_resource_map(struct virgl_winsys
*vws
,
34 struct virgl_hw_res
*res
);
35 static void virgl_vtest_resource_unmap(struct virgl_winsys
*vws
,
36 struct virgl_hw_res
*res
);
38 static inline boolean
can_cache_resource(struct virgl_hw_res
*res
)
40 return res
->cacheable
== TRUE
;
43 static uint32_t vtest_get_transfer_size(struct virgl_hw_res
*res
,
44 const struct pipe_box
*box
,
45 uint32_t stride
, uint32_t layer_stride
,
46 uint32_t level
, uint32_t *valid_stride_p
)
48 uint32_t valid_stride
, valid_layer_stride
;
50 valid_stride
= util_format_get_stride(res
->format
, box
->width
);
53 valid_stride
= stride
;
56 valid_layer_stride
= util_format_get_2d_size(res
->format
, valid_stride
,
60 valid_layer_stride
= layer_stride
;
63 *valid_stride_p
= valid_stride
;
64 return valid_layer_stride
* box
->depth
;
68 virgl_vtest_transfer_put(struct virgl_winsys
*vws
,
69 struct virgl_hw_res
*res
,
70 const struct pipe_box
*box
,
71 uint32_t stride
, uint32_t layer_stride
,
72 uint32_t buf_offset
, uint32_t level
)
74 struct virgl_vtest_winsys
*vtws
= virgl_vtest_winsys(vws
);
77 uint32_t valid_stride
;
79 size
= vtest_get_transfer_size(res
, box
, stride
, layer_stride
, level
,
82 virgl_vtest_send_transfer_cmd(vtws
, VCMD_TRANSFER_PUT
, res
->res_handle
,
83 level
, stride
, layer_stride
,
85 ptr
= virgl_vtest_resource_map(vws
, res
);
86 virgl_vtest_send_transfer_put_data(vtws
, ptr
+ buf_offset
, size
);
87 virgl_vtest_resource_unmap(vws
, res
);
92 virgl_vtest_transfer_get(struct virgl_winsys
*vws
,
93 struct virgl_hw_res
*res
,
94 const struct pipe_box
*box
,
95 uint32_t stride
, uint32_t layer_stride
,
96 uint32_t buf_offset
, uint32_t level
)
98 struct virgl_vtest_winsys
*vtws
= virgl_vtest_winsys(vws
);
101 uint32_t valid_stride
;
103 size
= vtest_get_transfer_size(res
, box
, stride
, layer_stride
, level
,
106 virgl_vtest_send_transfer_cmd(vtws
, VCMD_TRANSFER_GET
, res
->res_handle
,
107 level
, stride
, layer_stride
,
111 ptr
= virgl_vtest_resource_map(vws
, res
);
112 virgl_vtest_recv_transfer_get_data(vtws
, ptr
+ buf_offset
, size
,
113 valid_stride
, box
, res
->format
);
114 virgl_vtest_resource_unmap(vws
, res
);
118 static void virgl_hw_res_destroy(struct virgl_vtest_winsys
*vtws
,
119 struct virgl_hw_res
*res
)
121 virgl_vtest_send_resource_unref(vtws
, res
->res_handle
);
123 vtws
->sws
->displaytarget_destroy(vtws
->sws
, res
->dt
);
128 static boolean
virgl_vtest_resource_is_busy(struct virgl_vtest_winsys
*vtws
,
129 struct virgl_hw_res
*res
)
131 /* implement busy check */
133 ret
= virgl_vtest_busy_wait(vtws
, res
->res_handle
, 0);
138 return ret
== 1 ? TRUE
: FALSE
;
142 virgl_cache_flush(struct virgl_vtest_winsys
*vtws
)
144 struct list_head
*curr
, *next
;
145 struct virgl_hw_res
*res
;
147 pipe_mutex_lock(vtws
->mutex
);
148 curr
= vtws
->delayed
.next
;
151 while (curr
!= &vtws
->delayed
) {
152 res
= LIST_ENTRY(struct virgl_hw_res
, curr
, head
);
153 LIST_DEL(&res
->head
);
154 virgl_hw_res_destroy(vtws
, res
);
158 pipe_mutex_unlock(vtws
->mutex
);
162 virgl_cache_list_check_free(struct virgl_vtest_winsys
*vtws
)
164 struct list_head
*curr
, *next
;
165 struct virgl_hw_res
*res
;
169 curr
= vtws
->delayed
.next
;
171 while (curr
!= &vtws
->delayed
) {
172 res
= LIST_ENTRY(struct virgl_hw_res
, curr
, head
);
173 if (!os_time_timeout(res
->start
, res
->end
, now
))
176 LIST_DEL(&res
->head
);
177 virgl_hw_res_destroy(vtws
, res
);
183 static void virgl_vtest_resource_reference(struct virgl_vtest_winsys
*vtws
,
184 struct virgl_hw_res
**dres
,
185 struct virgl_hw_res
*sres
)
187 struct virgl_hw_res
*old
= *dres
;
188 if (pipe_reference(&(*dres
)->reference
, &sres
->reference
)) {
189 if (!can_cache_resource(old
)) {
190 virgl_hw_res_destroy(vtws
, old
);
192 pipe_mutex_lock(vtws
->mutex
);
193 virgl_cache_list_check_free(vtws
);
195 old
->start
= os_time_get();
196 old
->end
= old
->start
+ vtws
->usecs
;
197 LIST_ADDTAIL(&old
->head
, &vtws
->delayed
);
199 pipe_mutex_unlock(vtws
->mutex
);
205 static struct virgl_hw_res
*
206 virgl_vtest_winsys_resource_create(struct virgl_winsys
*vws
,
207 enum pipe_texture_target target
,
218 struct virgl_vtest_winsys
*vtws
= virgl_vtest_winsys(vws
);
219 struct virgl_hw_res
*res
;
220 static int handle
= 1;
222 res
= CALLOC_STRUCT(virgl_hw_res
);
226 if (bind
& (VIRGL_BIND_DISPLAY_TARGET
| VIRGL_BIND_SCANOUT
)) {
227 res
->dt
= vtws
->sws
->displaytarget_create(vtws
->sws
, bind
, format
,
228 width
, height
, 64, NULL
,
232 res
->ptr
= align_malloc(size
, 64);
240 res
->format
= format
;
241 res
->height
= height
;
243 virgl_vtest_send_resource_create(vtws
, handle
, target
, format
, bind
,
244 width
, height
, depth
, array_size
,
245 last_level
, nr_samples
);
247 res
->res_handle
= handle
++;
248 pipe_reference_init(&res
->reference
, 1);
252 static void virgl_vtest_winsys_resource_unref(struct virgl_winsys
*vws
,
253 struct virgl_hw_res
*hres
)
255 struct virgl_vtest_winsys
*vtws
= virgl_vtest_winsys(vws
);
256 virgl_vtest_resource_reference(vtws
, &hres
, NULL
);
259 static void *virgl_vtest_resource_map(struct virgl_winsys
*vws
,
260 struct virgl_hw_res
*res
)
262 struct virgl_vtest_winsys
*vtws
= virgl_vtest_winsys(vws
);
265 return vtws
->sws
->displaytarget_map(vtws
->sws
, res
->dt
, 0);
267 res
->mapped
= res
->ptr
;
272 static void virgl_vtest_resource_unmap(struct virgl_winsys
*vws
,
273 struct virgl_hw_res
*res
)
275 struct virgl_vtest_winsys
*vtws
= virgl_vtest_winsys(vws
);
280 vtws
->sws
->displaytarget_unmap(vtws
->sws
, res
->dt
);
283 static void virgl_vtest_resource_wait(struct virgl_winsys
*vws
,
284 struct virgl_hw_res
*res
)
286 struct virgl_vtest_winsys
*vtws
= virgl_vtest_winsys(vws
);
288 virgl_vtest_busy_wait(vtws
, res
->res_handle
, VCMD_BUSY_WAIT_FLAG_WAIT
);
291 static inline int virgl_is_res_compat(struct virgl_vtest_winsys
*vtws
,
292 struct virgl_hw_res
*res
,
293 uint32_t size
, uint32_t bind
,
296 if (res
->bind
!= bind
)
298 if (res
->format
!= format
)
300 if (res
->size
< size
)
302 if (res
->size
> size
* 2)
305 if (virgl_vtest_resource_is_busy(vtws
, res
)) {
312 static struct virgl_hw_res
*
313 virgl_vtest_winsys_resource_cache_create(struct virgl_winsys
*vws
,
314 enum pipe_texture_target target
,
325 struct virgl_vtest_winsys
*vtws
= virgl_vtest_winsys(vws
);
326 struct virgl_hw_res
*res
, *curr_res
;
327 struct list_head
*curr
, *next
;
331 /* only store binds for vertex/index/const buffers */
332 if (bind
!= VIRGL_BIND_CONSTANT_BUFFER
&& bind
!= VIRGL_BIND_INDEX_BUFFER
&&
333 bind
!= VIRGL_BIND_VERTEX_BUFFER
&& bind
!= VIRGL_BIND_CUSTOM
)
336 pipe_mutex_lock(vtws
->mutex
);
339 curr
= vtws
->delayed
.next
;
343 while (curr
!= &vtws
->delayed
) {
344 curr_res
= LIST_ENTRY(struct virgl_hw_res
, curr
, head
);
346 if (!res
&& ((ret
= virgl_is_res_compat(vtws
, curr_res
, size
, bind
, format
)) > 0))
348 else if (os_time_timeout(curr_res
->start
, curr_res
->end
, now
)) {
349 LIST_DEL(&curr_res
->head
);
350 virgl_hw_res_destroy(vtws
, curr_res
);
361 if (!res
&& ret
!= -1) {
362 while (curr
!= &vtws
->delayed
) {
363 curr_res
= LIST_ENTRY(struct virgl_hw_res
, curr
, head
);
364 ret
= virgl_is_res_compat(vtws
, curr_res
, size
, bind
, format
);
377 LIST_DEL(&res
->head
);
379 pipe_mutex_unlock(vtws
->mutex
);
380 pipe_reference_init(&res
->reference
, 1);
384 pipe_mutex_unlock(vtws
->mutex
);
387 res
= virgl_vtest_winsys_resource_create(vws
, target
, format
, bind
,
388 width
, height
, depth
, array_size
,
389 last_level
, nr_samples
, size
);
390 if (bind
== VIRGL_BIND_CONSTANT_BUFFER
|| bind
== VIRGL_BIND_INDEX_BUFFER
||
391 bind
== VIRGL_BIND_VERTEX_BUFFER
)
392 res
->cacheable
= TRUE
;
396 static struct virgl_cmd_buf
*virgl_vtest_cmd_buf_create(struct virgl_winsys
*vws
)
398 struct virgl_vtest_cmd_buf
*cbuf
;
400 cbuf
= CALLOC_STRUCT(virgl_vtest_cmd_buf
);
405 cbuf
->res_bo
= CALLOC(cbuf
->nres
, sizeof(struct virgl_hw_buf
*));
411 cbuf
->base
.buf
= cbuf
->buf
;
415 static void virgl_vtest_cmd_buf_destroy(struct virgl_cmd_buf
*_cbuf
)
417 struct virgl_vtest_cmd_buf
*cbuf
= virgl_vtest_cmd_buf(_cbuf
);
423 static boolean
virgl_vtest_lookup_res(struct virgl_vtest_cmd_buf
*cbuf
,
424 struct virgl_hw_res
*res
)
426 unsigned hash
= res
->res_handle
& (sizeof(cbuf
->is_handle_added
)-1);
429 if (cbuf
->is_handle_added
[hash
]) {
430 i
= cbuf
->reloc_indices_hashlist
[hash
];
431 if (cbuf
->res_bo
[i
] == res
)
434 for (i
= 0; i
< cbuf
->cres
; i
++) {
435 if (cbuf
->res_bo
[i
] == res
) {
436 cbuf
->reloc_indices_hashlist
[hash
] = i
;
444 static void virgl_vtest_release_all_res(struct virgl_vtest_winsys
*vtws
,
445 struct virgl_vtest_cmd_buf
*cbuf
)
449 for (i
= 0; i
< cbuf
->cres
; i
++) {
450 p_atomic_dec(&cbuf
->res_bo
[i
]->num_cs_references
);
451 virgl_vtest_resource_reference(vtws
, &cbuf
->res_bo
[i
], NULL
);
456 static void virgl_vtest_add_res(struct virgl_vtest_winsys
*vtws
,
457 struct virgl_vtest_cmd_buf
*cbuf
,
458 struct virgl_hw_res
*res
)
460 unsigned hash
= res
->res_handle
& (sizeof(cbuf
->is_handle_added
)-1);
462 if (cbuf
->cres
> cbuf
->nres
) {
463 fprintf(stderr
,"failure to add relocation\n");
467 cbuf
->res_bo
[cbuf
->cres
] = NULL
;
468 virgl_vtest_resource_reference(vtws
, &cbuf
->res_bo
[cbuf
->cres
], res
);
469 cbuf
->is_handle_added
[hash
] = TRUE
;
471 cbuf
->reloc_indices_hashlist
[hash
] = cbuf
->cres
;
472 p_atomic_inc(&res
->num_cs_references
);
476 static int virgl_vtest_winsys_submit_cmd(struct virgl_winsys
*vws
,
477 struct virgl_cmd_buf
*_cbuf
)
479 struct virgl_vtest_winsys
*vtws
= virgl_vtest_winsys(vws
);
480 struct virgl_vtest_cmd_buf
*cbuf
= virgl_vtest_cmd_buf(_cbuf
);
483 if (cbuf
->base
.cdw
== 0)
486 ret
= virgl_vtest_submit_cmd(vtws
, cbuf
);
488 virgl_vtest_release_all_res(vtws
, cbuf
);
489 memset(cbuf
->is_handle_added
, 0, sizeof(cbuf
->is_handle_added
));
494 static void virgl_vtest_emit_res(struct virgl_winsys
*vws
,
495 struct virgl_cmd_buf
*_cbuf
,
496 struct virgl_hw_res
*res
, boolean write_buf
)
498 struct virgl_vtest_winsys
*vtws
= virgl_vtest_winsys(vws
);
499 struct virgl_vtest_cmd_buf
*cbuf
= virgl_vtest_cmd_buf(_cbuf
);
500 boolean already_in_list
= virgl_vtest_lookup_res(cbuf
, res
);
503 cbuf
->base
.buf
[cbuf
->base
.cdw
++] = res
->res_handle
;
504 if (!already_in_list
)
505 virgl_vtest_add_res(vtws
, cbuf
, res
);
508 static boolean
virgl_vtest_res_is_ref(struct virgl_winsys
*vws
,
509 struct virgl_cmd_buf
*_cbuf
,
510 struct virgl_hw_res
*res
)
512 if (!res
->num_cs_references
)
518 static int virgl_vtest_get_caps(struct virgl_winsys
*vws
,
519 struct virgl_drm_caps
*caps
)
521 struct virgl_vtest_winsys
*vtws
= virgl_vtest_winsys(vws
);
522 return virgl_vtest_send_get_caps(vtws
, caps
);
525 static struct pipe_fence_handle
*
526 virgl_cs_create_fence(struct virgl_winsys
*vws
)
528 struct virgl_hw_res
*res
;
530 res
= virgl_vtest_winsys_resource_cache_create(vws
,
532 PIPE_FORMAT_R8_UNORM
,
534 8, 1, 1, 0, 0, 0, 8);
536 return (struct pipe_fence_handle
*)res
;
539 static bool virgl_fence_wait(struct virgl_winsys
*vws
,
540 struct pipe_fence_handle
*fence
,
543 struct virgl_vtest_winsys
*vdws
= virgl_vtest_winsys(vws
);
544 struct virgl_hw_res
*res
= virgl_hw_res(fence
);
547 return !virgl_vtest_resource_is_busy(vdws
, res
);
549 if (timeout
!= PIPE_TIMEOUT_INFINITE
) {
550 int64_t start_time
= os_time_get();
552 while (virgl_vtest_resource_is_busy(vdws
, res
)) {
553 if (os_time_get() - start_time
>= timeout
)
559 virgl_vtest_resource_wait(vws
, res
);
563 static void virgl_fence_reference(struct virgl_winsys
*vws
,
564 struct pipe_fence_handle
**dst
,
565 struct pipe_fence_handle
*src
)
567 struct virgl_vtest_winsys
*vdws
= virgl_vtest_winsys(vws
);
568 virgl_vtest_resource_reference(vdws
, (struct virgl_hw_res
**)dst
,
572 static void virgl_vtest_flush_frontbuffer(struct virgl_winsys
*vws
,
573 struct virgl_hw_res
*res
,
574 unsigned level
, unsigned layer
,
575 void *winsys_drawable_handle
,
576 struct pipe_box
*sub_box
)
578 struct virgl_vtest_winsys
*vtws
= virgl_vtest_winsys(vws
);
582 uint32_t offset
= 0, valid_stride
;
586 memset(&box
, 0, sizeof(box
));
590 offset
= box
.y
/ util_format_get_blockheight(res
->format
) * res
->stride
+
591 box
.x
/ util_format_get_blockwidth(res
->format
) * util_format_get_blocksize(res
->format
);
594 box
.width
= res
->width
;
595 box
.height
= res
->height
;
599 size
= vtest_get_transfer_size(res
, &box
, res
->stride
, 0, level
, &valid_stride
);
601 virgl_vtest_busy_wait(vtws
, res
->res_handle
, VCMD_BUSY_WAIT_FLAG_WAIT
);
602 map
= vtws
->sws
->displaytarget_map(vtws
->sws
, res
->dt
, 0);
604 /* execute a transfer */
605 virgl_vtest_send_transfer_cmd(vtws
, VCMD_TRANSFER_GET
, res
->res_handle
,
606 level
, res
->stride
, 0, &box
, size
);
607 virgl_vtest_recv_transfer_get_data(vtws
, map
+ offset
, size
, valid_stride
,
609 vtws
->sws
->displaytarget_unmap(vtws
->sws
, res
->dt
);
611 vtws
->sws
->displaytarget_display(vtws
->sws
, res
->dt
, winsys_drawable_handle
,
616 virgl_vtest_winsys_destroy(struct virgl_winsys
*vws
)
618 struct virgl_vtest_winsys
*vtws
= virgl_vtest_winsys(vws
);
620 virgl_cache_flush(vtws
);
622 pipe_mutex_destroy(vtws
->mutex
);
626 struct virgl_winsys
*
627 virgl_vtest_winsys_wrap(struct sw_winsys
*sws
)
629 struct virgl_vtest_winsys
*vtws
;
631 vtws
= CALLOC_STRUCT(virgl_vtest_winsys
);
635 virgl_vtest_connect(vtws
);
638 vtws
->usecs
= 1000000;
639 LIST_INITHEAD(&vtws
->delayed
);
640 pipe_mutex_init(vtws
->mutex
);
642 vtws
->base
.destroy
= virgl_vtest_winsys_destroy
;
644 vtws
->base
.transfer_put
= virgl_vtest_transfer_put
;
645 vtws
->base
.transfer_get
= virgl_vtest_transfer_get
;
647 vtws
->base
.resource_create
= virgl_vtest_winsys_resource_cache_create
;
648 vtws
->base
.resource_unref
= virgl_vtest_winsys_resource_unref
;
649 vtws
->base
.resource_map
= virgl_vtest_resource_map
;
650 vtws
->base
.resource_wait
= virgl_vtest_resource_wait
;
651 vtws
->base
.cmd_buf_create
= virgl_vtest_cmd_buf_create
;
652 vtws
->base
.cmd_buf_destroy
= virgl_vtest_cmd_buf_destroy
;
653 vtws
->base
.submit_cmd
= virgl_vtest_winsys_submit_cmd
;
655 vtws
->base
.emit_res
= virgl_vtest_emit_res
;
656 vtws
->base
.res_is_referenced
= virgl_vtest_res_is_ref
;
657 vtws
->base
.get_caps
= virgl_vtest_get_caps
;
659 vtws
->base
.cs_create_fence
= virgl_cs_create_fence
;
660 vtws
->base
.fence_wait
= virgl_fence_wait
;
661 vtws
->base
.fence_reference
= virgl_fence_reference
;
663 vtws
->base
.flush_frontbuffer
= virgl_vtest_flush_frontbuffer
;