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 "util/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 /* The size calculated above is the full box size, but if this box origin
83 * is not zero we may have to correct the transfer size to not read past the
84 * end of the resource. The correct adjustment depends on various factors
85 * that are not documented, so instead of going though all the hops to get
86 * the size right up-front, we just make sure we don't read past the end.
87 * FIXME: figure out what it takes to actually get this right.
89 if (size
+ buf_offset
> res
->size
)
90 size
= res
->size
- buf_offset
;
92 virgl_vtest_send_transfer_put(vtws
, res
->res_handle
,
93 level
, stride
, layer_stride
,
94 box
, size
, buf_offset
);
95 ptr
= virgl_vtest_resource_map(vws
, res
);
96 virgl_vtest_send_transfer_put_data(vtws
, ptr
+ buf_offset
, size
);
97 virgl_vtest_resource_unmap(vws
, res
);
102 virgl_vtest_transfer_get(struct virgl_winsys
*vws
,
103 struct virgl_hw_res
*res
,
104 const struct pipe_box
*box
,
105 uint32_t stride
, uint32_t layer_stride
,
106 uint32_t buf_offset
, uint32_t level
)
108 struct virgl_vtest_winsys
*vtws
= virgl_vtest_winsys(vws
);
111 uint32_t valid_stride
;
113 size
= vtest_get_transfer_size(res
, box
, stride
, layer_stride
, level
,
115 /* Don't ask for more pixels than available (see above) */
116 if (size
+ buf_offset
> res
->size
)
117 size
= res
->size
- buf_offset
;
119 virgl_vtest_send_transfer_get(vtws
, res
->res_handle
,
120 level
, stride
, layer_stride
,
121 box
, size
, buf_offset
);
123 ptr
= virgl_vtest_resource_map(vws
, res
);
125 /* This functions seems to be using a specific transfer resource that
126 * has exactly the box size and hence its src stride is equal to the target
128 virgl_vtest_recv_transfer_get_data(vtws
, ptr
+ buf_offset
, size
,
129 valid_stride
, box
, res
->format
, valid_stride
);
131 virgl_vtest_resource_unmap(vws
, res
);
135 static void virgl_hw_res_destroy(struct virgl_vtest_winsys
*vtws
,
136 struct virgl_hw_res
*res
)
138 virgl_vtest_send_resource_unref(vtws
, res
->res_handle
);
140 vtws
->sws
->displaytarget_destroy(vtws
->sws
, res
->dt
);
145 static boolean
virgl_vtest_resource_is_busy(struct virgl_vtest_winsys
*vtws
,
146 struct virgl_hw_res
*res
)
148 /* implement busy check */
150 ret
= virgl_vtest_busy_wait(vtws
, res
->res_handle
, 0);
155 return ret
== 1 ? TRUE
: FALSE
;
159 virgl_cache_flush(struct virgl_vtest_winsys
*vtws
)
161 struct list_head
*curr
, *next
;
162 struct virgl_hw_res
*res
;
164 mtx_lock(&vtws
->mutex
);
165 curr
= vtws
->delayed
.next
;
168 while (curr
!= &vtws
->delayed
) {
169 res
= LIST_ENTRY(struct virgl_hw_res
, curr
, head
);
170 LIST_DEL(&res
->head
);
171 virgl_hw_res_destroy(vtws
, res
);
175 mtx_unlock(&vtws
->mutex
);
179 virgl_cache_list_check_free(struct virgl_vtest_winsys
*vtws
)
181 struct list_head
*curr
, *next
;
182 struct virgl_hw_res
*res
;
186 curr
= vtws
->delayed
.next
;
188 while (curr
!= &vtws
->delayed
) {
189 res
= LIST_ENTRY(struct virgl_hw_res
, curr
, head
);
190 if (!os_time_timeout(res
->start
, res
->end
, now
))
193 LIST_DEL(&res
->head
);
194 virgl_hw_res_destroy(vtws
, res
);
200 static void virgl_vtest_resource_reference(struct virgl_vtest_winsys
*vtws
,
201 struct virgl_hw_res
**dres
,
202 struct virgl_hw_res
*sres
)
204 struct virgl_hw_res
*old
= *dres
;
205 if (pipe_reference(&(*dres
)->reference
, &sres
->reference
)) {
206 if (!can_cache_resource(old
)) {
207 virgl_hw_res_destroy(vtws
, old
);
209 mtx_lock(&vtws
->mutex
);
210 virgl_cache_list_check_free(vtws
);
212 old
->start
= os_time_get();
213 old
->end
= old
->start
+ vtws
->usecs
;
214 LIST_ADDTAIL(&old
->head
, &vtws
->delayed
);
216 mtx_unlock(&vtws
->mutex
);
222 static struct virgl_hw_res
*
223 virgl_vtest_winsys_resource_create(struct virgl_winsys
*vws
,
224 enum pipe_texture_target target
,
235 struct virgl_vtest_winsys
*vtws
= virgl_vtest_winsys(vws
);
236 struct virgl_hw_res
*res
;
237 static int handle
= 1;
239 res
= CALLOC_STRUCT(virgl_hw_res
);
243 if (bind
& (VIRGL_BIND_DISPLAY_TARGET
| VIRGL_BIND_SCANOUT
)) {
244 res
->dt
= vtws
->sws
->displaytarget_create(vtws
->sws
, bind
, format
,
245 width
, height
, 64, NULL
,
249 res
->ptr
= align_malloc(size
, 64);
257 res
->format
= format
;
258 res
->height
= height
;
261 virgl_vtest_send_resource_create(vtws
, handle
, target
, format
, bind
,
262 width
, height
, depth
, array_size
,
263 last_level
, nr_samples
, size
);
265 res
->res_handle
= handle
++;
266 pipe_reference_init(&res
->reference
, 1);
270 static void virgl_vtest_winsys_resource_unref(struct virgl_winsys
*vws
,
271 struct virgl_hw_res
*hres
)
273 struct virgl_vtest_winsys
*vtws
= virgl_vtest_winsys(vws
);
274 virgl_vtest_resource_reference(vtws
, &hres
, NULL
);
277 static void *virgl_vtest_resource_map(struct virgl_winsys
*vws
,
278 struct virgl_hw_res
*res
)
280 struct virgl_vtest_winsys
*vtws
= virgl_vtest_winsys(vws
);
283 return vtws
->sws
->displaytarget_map(vtws
->sws
, res
->dt
, 0);
285 res
->mapped
= res
->ptr
;
290 static void virgl_vtest_resource_unmap(struct virgl_winsys
*vws
,
291 struct virgl_hw_res
*res
)
293 struct virgl_vtest_winsys
*vtws
= virgl_vtest_winsys(vws
);
298 vtws
->sws
->displaytarget_unmap(vtws
->sws
, res
->dt
);
301 static void virgl_vtest_resource_wait(struct virgl_winsys
*vws
,
302 struct virgl_hw_res
*res
)
304 struct virgl_vtest_winsys
*vtws
= virgl_vtest_winsys(vws
);
306 virgl_vtest_busy_wait(vtws
, res
->res_handle
, VCMD_BUSY_WAIT_FLAG_WAIT
);
309 static inline int virgl_is_res_compat(struct virgl_vtest_winsys
*vtws
,
310 struct virgl_hw_res
*res
,
311 uint32_t size
, uint32_t bind
,
314 if (res
->bind
!= bind
)
316 if (res
->format
!= format
)
318 if (res
->size
< size
)
320 if (res
->size
> size
* 2)
323 if (virgl_vtest_resource_is_busy(vtws
, res
)) {
330 static struct virgl_hw_res
*
331 virgl_vtest_winsys_resource_cache_create(struct virgl_winsys
*vws
,
332 enum pipe_texture_target target
,
343 struct virgl_vtest_winsys
*vtws
= virgl_vtest_winsys(vws
);
344 struct virgl_hw_res
*res
, *curr_res
;
345 struct list_head
*curr
, *next
;
349 /* only store binds for vertex/index/const buffers */
350 if (bind
!= VIRGL_BIND_CONSTANT_BUFFER
&& bind
!= VIRGL_BIND_INDEX_BUFFER
&&
351 bind
!= VIRGL_BIND_VERTEX_BUFFER
&& bind
!= VIRGL_BIND_CUSTOM
)
354 mtx_lock(&vtws
->mutex
);
357 curr
= vtws
->delayed
.next
;
361 while (curr
!= &vtws
->delayed
) {
362 curr_res
= LIST_ENTRY(struct virgl_hw_res
, curr
, head
);
364 if (!res
&& ((ret
= virgl_is_res_compat(vtws
, curr_res
, size
, bind
, format
)) > 0))
366 else if (os_time_timeout(curr_res
->start
, curr_res
->end
, now
)) {
367 LIST_DEL(&curr_res
->head
);
368 virgl_hw_res_destroy(vtws
, curr_res
);
379 if (!res
&& ret
!= -1) {
380 while (curr
!= &vtws
->delayed
) {
381 curr_res
= LIST_ENTRY(struct virgl_hw_res
, curr
, head
);
382 ret
= virgl_is_res_compat(vtws
, curr_res
, size
, bind
, format
);
395 LIST_DEL(&res
->head
);
397 mtx_unlock(&vtws
->mutex
);
398 pipe_reference_init(&res
->reference
, 1);
402 mtx_unlock(&vtws
->mutex
);
405 res
= virgl_vtest_winsys_resource_create(vws
, target
, format
, bind
,
406 width
, height
, depth
, array_size
,
407 last_level
, nr_samples
, size
);
408 if (bind
== VIRGL_BIND_CONSTANT_BUFFER
|| bind
== VIRGL_BIND_INDEX_BUFFER
||
409 bind
== VIRGL_BIND_VERTEX_BUFFER
)
410 res
->cacheable
= TRUE
;
414 static struct virgl_cmd_buf
*virgl_vtest_cmd_buf_create(struct virgl_winsys
*vws
)
416 struct virgl_vtest_cmd_buf
*cbuf
;
418 cbuf
= CALLOC_STRUCT(virgl_vtest_cmd_buf
);
423 cbuf
->res_bo
= CALLOC(cbuf
->nres
, sizeof(struct virgl_hw_buf
*));
429 cbuf
->base
.buf
= cbuf
->buf
;
430 cbuf
->base
.in_fence_fd
= -1;
434 static void virgl_vtest_cmd_buf_destroy(struct virgl_cmd_buf
*_cbuf
)
436 struct virgl_vtest_cmd_buf
*cbuf
= virgl_vtest_cmd_buf(_cbuf
);
442 static boolean
virgl_vtest_lookup_res(struct virgl_vtest_cmd_buf
*cbuf
,
443 struct virgl_hw_res
*res
)
445 unsigned hash
= res
->res_handle
& (sizeof(cbuf
->is_handle_added
)-1);
448 if (cbuf
->is_handle_added
[hash
]) {
449 i
= cbuf
->reloc_indices_hashlist
[hash
];
450 if (cbuf
->res_bo
[i
] == res
)
453 for (i
= 0; i
< cbuf
->cres
; i
++) {
454 if (cbuf
->res_bo
[i
] == res
) {
455 cbuf
->reloc_indices_hashlist
[hash
] = i
;
463 static void virgl_vtest_release_all_res(struct virgl_vtest_winsys
*vtws
,
464 struct virgl_vtest_cmd_buf
*cbuf
)
468 for (i
= 0; i
< cbuf
->cres
; i
++) {
469 p_atomic_dec(&cbuf
->res_bo
[i
]->num_cs_references
);
470 virgl_vtest_resource_reference(vtws
, &cbuf
->res_bo
[i
], NULL
);
475 static void virgl_vtest_add_res(struct virgl_vtest_winsys
*vtws
,
476 struct virgl_vtest_cmd_buf
*cbuf
,
477 struct virgl_hw_res
*res
)
479 unsigned hash
= res
->res_handle
& (sizeof(cbuf
->is_handle_added
)-1);
481 if (cbuf
->cres
>= cbuf
->nres
) {
482 unsigned new_nres
= cbuf
->nres
+ 256;
483 struct virgl_hw_res
**new_re_bo
= REALLOC(cbuf
->res_bo
,
484 cbuf
->nres
* sizeof(struct virgl_hw_buf
*),
485 new_nres
* sizeof(struct virgl_hw_buf
*));
487 fprintf(stderr
,"failure to add relocation %d, %d\n", cbuf
->cres
, cbuf
->nres
);
491 cbuf
->res_bo
= new_re_bo
;
492 cbuf
->nres
= new_nres
;
495 cbuf
->res_bo
[cbuf
->cres
] = NULL
;
496 virgl_vtest_resource_reference(vtws
, &cbuf
->res_bo
[cbuf
->cres
], res
);
497 cbuf
->is_handle_added
[hash
] = TRUE
;
499 cbuf
->reloc_indices_hashlist
[hash
] = cbuf
->cres
;
500 p_atomic_inc(&res
->num_cs_references
);
504 static int virgl_vtest_winsys_submit_cmd(struct virgl_winsys
*vws
,
505 struct virgl_cmd_buf
*_cbuf
,
506 int in_fence_fd
, int *out_fence_fd
)
508 struct virgl_vtest_winsys
*vtws
= virgl_vtest_winsys(vws
);
509 struct virgl_vtest_cmd_buf
*cbuf
= virgl_vtest_cmd_buf(_cbuf
);
512 if (cbuf
->base
.cdw
== 0)
515 assert(in_fence_fd
== -1);
516 assert(out_fence_fd
== NULL
);
518 ret
= virgl_vtest_submit_cmd(vtws
, cbuf
);
520 virgl_vtest_release_all_res(vtws
, cbuf
);
521 memset(cbuf
->is_handle_added
, 0, sizeof(cbuf
->is_handle_added
));
526 static void virgl_vtest_emit_res(struct virgl_winsys
*vws
,
527 struct virgl_cmd_buf
*_cbuf
,
528 struct virgl_hw_res
*res
, boolean write_buf
)
530 struct virgl_vtest_winsys
*vtws
= virgl_vtest_winsys(vws
);
531 struct virgl_vtest_cmd_buf
*cbuf
= virgl_vtest_cmd_buf(_cbuf
);
532 boolean already_in_list
= virgl_vtest_lookup_res(cbuf
, res
);
535 cbuf
->base
.buf
[cbuf
->base
.cdw
++] = res
->res_handle
;
536 if (!already_in_list
)
537 virgl_vtest_add_res(vtws
, cbuf
, res
);
540 static boolean
virgl_vtest_res_is_ref(struct virgl_winsys
*vws
,
541 struct virgl_cmd_buf
*_cbuf
,
542 struct virgl_hw_res
*res
)
544 if (!res
->num_cs_references
)
550 static int virgl_vtest_get_caps(struct virgl_winsys
*vws
,
551 struct virgl_drm_caps
*caps
)
553 struct virgl_vtest_winsys
*vtws
= virgl_vtest_winsys(vws
);
555 virgl_ws_fill_new_caps_defaults(caps
);
556 return virgl_vtest_send_get_caps(vtws
, caps
);
559 static struct pipe_fence_handle
*
560 virgl_cs_create_fence(struct virgl_winsys
*vws
, int fd
)
562 struct virgl_hw_res
*res
;
564 res
= virgl_vtest_winsys_resource_cache_create(vws
,
566 PIPE_FORMAT_R8_UNORM
,
568 8, 1, 1, 0, 0, 0, 8);
570 return (struct pipe_fence_handle
*)res
;
573 static bool virgl_fence_wait(struct virgl_winsys
*vws
,
574 struct pipe_fence_handle
*fence
,
577 struct virgl_vtest_winsys
*vdws
= virgl_vtest_winsys(vws
);
578 struct virgl_hw_res
*res
= virgl_hw_res(fence
);
581 return !virgl_vtest_resource_is_busy(vdws
, res
);
583 if (timeout
!= PIPE_TIMEOUT_INFINITE
) {
584 int64_t start_time
= os_time_get();
586 while (virgl_vtest_resource_is_busy(vdws
, res
)) {
587 if (os_time_get() - start_time
>= timeout
)
593 virgl_vtest_resource_wait(vws
, res
);
597 static void virgl_fence_reference(struct virgl_winsys
*vws
,
598 struct pipe_fence_handle
**dst
,
599 struct pipe_fence_handle
*src
)
601 struct virgl_vtest_winsys
*vdws
= virgl_vtest_winsys(vws
);
602 virgl_vtest_resource_reference(vdws
, (struct virgl_hw_res
**)dst
,
606 static void virgl_vtest_flush_frontbuffer(struct virgl_winsys
*vws
,
607 struct virgl_hw_res
*res
,
608 unsigned level
, unsigned layer
,
609 void *winsys_drawable_handle
,
610 struct pipe_box
*sub_box
)
612 struct virgl_vtest_winsys
*vtws
= virgl_vtest_winsys(vws
);
616 uint32_t offset
= 0, valid_stride
;
620 memset(&box
, 0, sizeof(box
));
624 offset
= box
.y
/ util_format_get_blockheight(res
->format
) * res
->stride
+
625 box
.x
/ util_format_get_blockwidth(res
->format
) * util_format_get_blocksize(res
->format
);
628 box
.width
= res
->width
;
629 box
.height
= res
->height
;
633 size
= vtest_get_transfer_size(res
, &box
, res
->stride
, 0, level
, &valid_stride
);
635 virgl_vtest_busy_wait(vtws
, res
->res_handle
, VCMD_BUSY_WAIT_FLAG_WAIT
);
636 map
= vtws
->sws
->displaytarget_map(vtws
->sws
, res
->dt
, 0);
638 /* execute a transfer */
639 virgl_vtest_send_transfer_get(vtws
, res
->res_handle
,
640 level
, res
->stride
, 0, &box
, size
, offset
);
642 /* This functions gets the resource from the hardware backend that may have
643 * a hardware imposed stride that is different from the IOV stride used to
645 virgl_vtest_recv_transfer_get_data(vtws
, map
+ offset
, size
, valid_stride
,
647 util_format_get_stride(res
->format
, res
->width
));
649 vtws
->sws
->displaytarget_unmap(vtws
->sws
, res
->dt
);
651 vtws
->sws
->displaytarget_display(vtws
->sws
, res
->dt
, winsys_drawable_handle
,
656 virgl_vtest_winsys_destroy(struct virgl_winsys
*vws
)
658 struct virgl_vtest_winsys
*vtws
= virgl_vtest_winsys(vws
);
660 virgl_cache_flush(vtws
);
662 mtx_destroy(&vtws
->mutex
);
666 struct virgl_winsys
*
667 virgl_vtest_winsys_wrap(struct sw_winsys
*sws
)
669 struct virgl_vtest_winsys
*vtws
;
671 vtws
= CALLOC_STRUCT(virgl_vtest_winsys
);
675 virgl_vtest_connect(vtws
);
678 vtws
->usecs
= 1000000;
679 LIST_INITHEAD(&vtws
->delayed
);
680 (void) mtx_init(&vtws
->mutex
, mtx_plain
);
682 vtws
->base
.destroy
= virgl_vtest_winsys_destroy
;
684 vtws
->base
.transfer_put
= virgl_vtest_transfer_put
;
685 vtws
->base
.transfer_get
= virgl_vtest_transfer_get
;
687 vtws
->base
.resource_create
= virgl_vtest_winsys_resource_cache_create
;
688 vtws
->base
.resource_unref
= virgl_vtest_winsys_resource_unref
;
689 vtws
->base
.resource_map
= virgl_vtest_resource_map
;
690 vtws
->base
.resource_wait
= virgl_vtest_resource_wait
;
691 vtws
->base
.cmd_buf_create
= virgl_vtest_cmd_buf_create
;
692 vtws
->base
.cmd_buf_destroy
= virgl_vtest_cmd_buf_destroy
;
693 vtws
->base
.submit_cmd
= virgl_vtest_winsys_submit_cmd
;
695 vtws
->base
.emit_res
= virgl_vtest_emit_res
;
696 vtws
->base
.res_is_referenced
= virgl_vtest_res_is_ref
;
697 vtws
->base
.get_caps
= virgl_vtest_get_caps
;
699 vtws
->base
.cs_create_fence
= virgl_cs_create_fence
;
700 vtws
->base
.fence_wait
= virgl_fence_wait
;
701 vtws
->base
.fence_reference
= virgl_fence_reference
;
702 vtws
->base
.supports_fences
= 0;
704 vtws
->base
.flush_frontbuffer
= virgl_vtest_flush_frontbuffer
;