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 "virgl_vtest_winsys.h"
25 #include "virgl_vtest_public.h"
26 #include "util/u_memory.h"
27 #include "util/u_format.h"
28 #include "util/u_inlines.h"
29 #include "state_tracker/drm_driver.h"
30 #include "os/os_time.h"
32 static void *virgl_vtest_resource_map(struct virgl_winsys
*vws
, struct virgl_hw_res
*res
);
33 static void virgl_vtest_resource_unmap(struct virgl_winsys
*vws
, struct virgl_hw_res
*res
);
34 static inline boolean
can_cache_resource(struct virgl_hw_res
*res
)
36 return res
->cacheable
== TRUE
;
39 static uint32_t vtest_get_transfer_size(struct virgl_hw_res
*res
,
40 const struct pipe_box
*box
,
41 uint32_t stride
, uint32_t layer_stride
,
42 uint32_t level
, uint32_t *valid_stride_p
)
44 uint32_t valid_stride
, valid_layer_stride
;
46 valid_stride
= util_format_get_stride(res
->format
, box
->width
);
49 valid_stride
= stride
;
52 valid_layer_stride
= util_format_get_2d_size(res
->format
, valid_stride
,
56 valid_layer_stride
= layer_stride
;
59 *valid_stride_p
= valid_stride
;
60 return valid_layer_stride
* box
->depth
;
64 virgl_vtest_transfer_put(struct virgl_winsys
*vws
,
65 struct virgl_hw_res
*res
,
66 const struct pipe_box
*box
,
67 uint32_t stride
, uint32_t layer_stride
,
68 uint32_t buf_offset
, uint32_t level
)
70 struct virgl_vtest_winsys
*vtws
= virgl_vtest_winsys(vws
);
73 uint32_t valid_stride
;
75 size
= vtest_get_transfer_size(res
, box
, stride
, layer_stride
, level
, &valid_stride
);
77 virgl_vtest_send_transfer_cmd(vtws
, VCMD_TRANSFER_PUT
, res
->res_handle
,
78 level
, stride
, layer_stride
,
80 ptr
= virgl_vtest_resource_map(vws
, res
);
81 virgl_vtest_send_transfer_put_data(vtws
, ptr
+ buf_offset
, size
);
82 virgl_vtest_resource_unmap(vws
, res
);
87 virgl_vtest_transfer_get(struct virgl_winsys
*vws
,
88 struct virgl_hw_res
*res
,
89 const struct pipe_box
*box
,
90 uint32_t stride
, uint32_t layer_stride
,
91 uint32_t buf_offset
, uint32_t level
)
93 struct virgl_vtest_winsys
*vtws
= virgl_vtest_winsys(vws
);
96 uint32_t valid_stride
;
98 size
= vtest_get_transfer_size(res
, box
, stride
, layer_stride
, level
, &valid_stride
);
100 virgl_vtest_send_transfer_cmd(vtws
, VCMD_TRANSFER_GET
, res
->res_handle
,
101 level
, stride
, layer_stride
,
105 ptr
= virgl_vtest_resource_map(vws
, res
);
106 virgl_vtest_recv_transfer_get_data(vtws
, ptr
+ buf_offset
, size
, valid_stride
, box
, res
->format
);
107 virgl_vtest_resource_unmap(vws
, res
);
111 static void virgl_hw_res_destroy(struct virgl_vtest_winsys
*vtws
,
112 struct virgl_hw_res
*res
)
114 virgl_vtest_send_resource_unref(vtws
, res
->res_handle
);
116 vtws
->sws
->displaytarget_destroy(vtws
->sws
, res
->dt
);
121 static boolean
virgl_vtest_resource_is_busy(struct virgl_vtest_winsys
*vtws
,
122 struct virgl_hw_res
*res
)
124 /* implement busy check */
126 ret
= virgl_vtest_busy_wait(vtws
, res
->res_handle
, 0);
131 return ret
== 1 ? TRUE
: FALSE
;
135 virgl_cache_flush(struct virgl_vtest_winsys
*vtws
)
137 struct list_head
*curr
, *next
;
138 struct virgl_hw_res
*res
;
140 pipe_mutex_lock(vtws
->mutex
);
141 curr
= vtws
->delayed
.next
;
144 while (curr
!= &vtws
->delayed
) {
145 res
= LIST_ENTRY(struct virgl_hw_res
, curr
, head
);
146 LIST_DEL(&res
->head
);
147 virgl_hw_res_destroy(vtws
, res
);
151 pipe_mutex_unlock(vtws
->mutex
);
155 virgl_cache_list_check_free(struct virgl_vtest_winsys
*vtws
)
157 struct list_head
*curr
, *next
;
158 struct virgl_hw_res
*res
;
162 curr
= vtws
->delayed
.next
;
164 while (curr
!= &vtws
->delayed
) {
165 res
= LIST_ENTRY(struct virgl_hw_res
, curr
, head
);
166 if (!os_time_timeout(res
->start
, res
->end
, now
))
169 LIST_DEL(&res
->head
);
170 virgl_hw_res_destroy(vtws
, res
);
176 static void virgl_vtest_resource_reference(struct virgl_vtest_winsys
*vtws
,
177 struct virgl_hw_res
**dres
,
178 struct virgl_hw_res
*sres
)
180 struct virgl_hw_res
*old
= *dres
;
181 if (pipe_reference(&(*dres
)->reference
, &sres
->reference
)) {
182 if (!can_cache_resource(old
)) {
183 virgl_hw_res_destroy(vtws
, old
);
185 pipe_mutex_lock(vtws
->mutex
);
186 virgl_cache_list_check_free(vtws
);
188 old
->start
= os_time_get();
189 old
->end
= old
->start
+ vtws
->usecs
;
190 LIST_ADDTAIL(&old
->head
, &vtws
->delayed
);
192 pipe_mutex_unlock(vtws
->mutex
);
198 static struct virgl_hw_res
*virgl_vtest_winsys_resource_create(
199 struct virgl_winsys
*vws
,
200 enum pipe_texture_target target
,
211 struct virgl_vtest_winsys
*vtws
= virgl_vtest_winsys(vws
);
212 struct virgl_hw_res
*res
;
213 static int handle
= 1;
215 res
= CALLOC_STRUCT(virgl_hw_res
);
219 if (bind
& (VIRGL_BIND_DISPLAY_TARGET
| VIRGL_BIND_SCANOUT
)) {
220 res
->dt
= vtws
->sws
->displaytarget_create(vtws
->sws
,
229 res
->ptr
= align_malloc(size
, 64);
237 res
->format
= format
;
238 res
->height
= height
;
240 virgl_vtest_send_resource_create(vtws
, handle
, target
, format
, bind
, width
,
241 height
, depth
, array_size
, last_level
,
244 res
->res_handle
= handle
++;
245 pipe_reference_init(&res
->reference
, 1);
249 static void virgl_vtest_winsys_resource_unref(struct virgl_winsys
*vws
,
250 struct virgl_hw_res
*hres
)
252 struct virgl_vtest_winsys
*vtws
= virgl_vtest_winsys(vws
);
253 virgl_vtest_resource_reference(vtws
, &hres
, NULL
);
256 static void *virgl_vtest_resource_map(struct virgl_winsys
*vws
, struct virgl_hw_res
*res
)
258 struct virgl_vtest_winsys
*vtws
= virgl_vtest_winsys(vws
);
261 return vtws
->sws
->displaytarget_map(vtws
->sws
, res
->dt
, 0);
263 res
->mapped
= res
->ptr
;
268 static void virgl_vtest_resource_unmap(struct virgl_winsys
*vws
, struct virgl_hw_res
*res
)
270 struct virgl_vtest_winsys
*vtws
= virgl_vtest_winsys(vws
);
275 vtws
->sws
->displaytarget_unmap(vtws
->sws
, res
->dt
);
278 static void virgl_vtest_resource_wait(struct virgl_winsys
*vws
, struct virgl_hw_res
*res
)
280 struct virgl_vtest_winsys
*vtws
= virgl_vtest_winsys(vws
);
282 virgl_vtest_busy_wait(vtws
, res
->res_handle
, VCMD_BUSY_WAIT_FLAG_WAIT
);
285 static inline int virgl_is_res_compat(struct virgl_vtest_winsys
*vtws
,
286 struct virgl_hw_res
*res
,
287 uint32_t size
, uint32_t bind
, uint32_t format
)
289 if (res
->bind
!= bind
)
291 if (res
->format
!= format
)
293 if (res
->size
< size
)
295 if (res
->size
> size
* 2)
298 if (virgl_vtest_resource_is_busy(vtws
, res
)) {
305 static struct virgl_hw_res
*virgl_vtest_winsys_resource_cache_create(struct virgl_winsys
*vws
,
306 enum pipe_texture_target target
,
317 struct virgl_vtest_winsys
*vtws
= virgl_vtest_winsys(vws
);
318 struct virgl_hw_res
*res
, *curr_res
;
319 struct list_head
*curr
, *next
;
323 /* only store binds for vertex/index/const buffers */
324 if (bind
!= VIRGL_BIND_CONSTANT_BUFFER
&& bind
!= VIRGL_BIND_INDEX_BUFFER
&&
325 bind
!= VIRGL_BIND_VERTEX_BUFFER
&& bind
!= VIRGL_BIND_CUSTOM
)
328 pipe_mutex_lock(vtws
->mutex
);
331 curr
= vtws
->delayed
.next
;
335 while (curr
!= &vtws
->delayed
) {
336 curr_res
= LIST_ENTRY(struct virgl_hw_res
, curr
, head
);
338 if (!res
&& (ret
= virgl_is_res_compat(vtws
, curr_res
, size
, bind
, format
) > 0))
340 else if (os_time_timeout(curr_res
->start
, curr_res
->end
, now
)) {
341 LIST_DEL(&curr_res
->head
);
342 virgl_hw_res_destroy(vtws
, curr_res
);
353 if (!res
&& ret
!= -1) {
354 while (curr
!= &vtws
->delayed
) {
355 curr_res
= LIST_ENTRY(struct virgl_hw_res
, curr
, head
);
356 ret
= virgl_is_res_compat(vtws
, curr_res
, size
, bind
, format
);
369 LIST_DEL(&res
->head
);
371 pipe_mutex_unlock(vtws
->mutex
);
372 pipe_reference_init(&res
->reference
, 1);
376 pipe_mutex_unlock(vtws
->mutex
);
379 res
= virgl_vtest_winsys_resource_create(vws
, target
, format
, bind
,
380 width
, height
, depth
, array_size
,
381 last_level
, nr_samples
, size
);
382 if (bind
== VIRGL_BIND_CONSTANT_BUFFER
|| bind
== VIRGL_BIND_INDEX_BUFFER
||
383 bind
== VIRGL_BIND_VERTEX_BUFFER
)
384 res
->cacheable
= TRUE
;
388 static struct virgl_cmd_buf
*virgl_vtest_cmd_buf_create(struct virgl_winsys
*vws
)
390 struct virgl_vtest_cmd_buf
*cbuf
;
392 cbuf
= CALLOC_STRUCT(virgl_vtest_cmd_buf
);
397 cbuf
->res_bo
= CALLOC(cbuf
->nres
, sizeof(struct virgl_hw_buf
*));
403 cbuf
->base
.buf
= cbuf
->buf
;
407 static void virgl_vtest_cmd_buf_destroy(struct virgl_cmd_buf
*_cbuf
)
409 struct virgl_vtest_cmd_buf
*cbuf
= virgl_vtest_cmd_buf(_cbuf
);
415 static boolean
virgl_vtest_lookup_res(struct virgl_vtest_cmd_buf
*cbuf
,
416 struct virgl_hw_res
*res
)
418 unsigned hash
= res
->res_handle
& (sizeof(cbuf
->is_handle_added
)-1);
421 if (cbuf
->is_handle_added
[hash
]) {
422 i
= cbuf
->reloc_indices_hashlist
[hash
];
423 if (cbuf
->res_bo
[i
] == res
)
426 for (i
= 0; i
< cbuf
->cres
; i
++) {
427 if (cbuf
->res_bo
[i
] == res
) {
428 cbuf
->reloc_indices_hashlist
[hash
] = i
;
436 static void virgl_vtest_release_all_res(struct virgl_vtest_winsys
*vtws
,
437 struct virgl_vtest_cmd_buf
*cbuf
)
441 for (i
= 0; i
< cbuf
->cres
; i
++) {
442 p_atomic_dec(&cbuf
->res_bo
[i
]->num_cs_references
);
443 virgl_vtest_resource_reference(vtws
, &cbuf
->res_bo
[i
], NULL
);
448 static void virgl_vtest_add_res(struct virgl_vtest_winsys
*vtws
,
449 struct virgl_vtest_cmd_buf
*cbuf
, struct virgl_hw_res
*res
)
451 unsigned hash
= res
->res_handle
& (sizeof(cbuf
->is_handle_added
)-1);
453 if (cbuf
->cres
> cbuf
->nres
) {
454 fprintf(stderr
,"failure to add relocation\n");
458 cbuf
->res_bo
[cbuf
->cres
] = NULL
;
459 virgl_vtest_resource_reference(vtws
, &cbuf
->res_bo
[cbuf
->cres
], res
);
460 cbuf
->is_handle_added
[hash
] = TRUE
;
462 cbuf
->reloc_indices_hashlist
[hash
] = cbuf
->cres
;
463 p_atomic_inc(&res
->num_cs_references
);
467 static int virgl_vtest_winsys_submit_cmd(struct virgl_winsys
*vws
, struct virgl_cmd_buf
*_cbuf
)
469 struct virgl_vtest_winsys
*vtws
= virgl_vtest_winsys(vws
);
470 struct virgl_vtest_cmd_buf
*cbuf
= virgl_vtest_cmd_buf(_cbuf
);
473 if (cbuf
->base
.cdw
== 0)
476 ret
= virgl_vtest_submit_cmd(vtws
, cbuf
);
478 virgl_vtest_release_all_res(vtws
, cbuf
);
479 memset(cbuf
->is_handle_added
, 0, sizeof(cbuf
->is_handle_added
));
484 static void virgl_vtest_emit_res(struct virgl_winsys
*vws
, struct virgl_cmd_buf
*_cbuf
, struct virgl_hw_res
*res
, boolean write_buf
)
486 struct virgl_vtest_winsys
*vtws
= virgl_vtest_winsys(vws
);
487 struct virgl_vtest_cmd_buf
*cbuf
= virgl_vtest_cmd_buf(_cbuf
);
488 boolean already_in_list
= virgl_vtest_lookup_res(cbuf
, res
);
491 cbuf
->base
.buf
[cbuf
->base
.cdw
++] = res
->res_handle
;
492 if (!already_in_list
)
493 virgl_vtest_add_res(vtws
, cbuf
, res
);
496 static boolean
virgl_vtest_res_is_ref(struct virgl_winsys
*vws
,
497 struct virgl_cmd_buf
*_cbuf
,
498 struct virgl_hw_res
*res
)
500 if (!res
->num_cs_references
)
506 static int virgl_vtest_get_caps(struct virgl_winsys
*vws
, struct virgl_drm_caps
*caps
)
508 struct virgl_vtest_winsys
*vtws
= virgl_vtest_winsys(vws
);
509 return virgl_vtest_send_get_caps(vtws
, caps
);
512 static struct pipe_fence_handle
*
513 virgl_cs_create_fence(struct virgl_winsys
*vws
)
515 struct virgl_hw_res
*res
;
517 res
= virgl_vtest_winsys_resource_cache_create(vws
,
519 PIPE_FORMAT_R8_UNORM
,
521 8, 1, 1, 0, 0, 0, 8);
523 return (struct pipe_fence_handle
*)res
;
526 static bool virgl_fence_wait(struct virgl_winsys
*vws
,
527 struct pipe_fence_handle
*fence
,
530 struct virgl_vtest_winsys
*vdws
= virgl_vtest_winsys(vws
);
531 struct virgl_hw_res
*res
= virgl_hw_res(fence
);
534 return virgl_vtest_resource_is_busy(vdws
, res
);
536 if (timeout
!= PIPE_TIMEOUT_INFINITE
) {
537 int64_t start_time
= os_time_get();
539 while (virgl_vtest_resource_is_busy(vdws
, res
)) {
540 if (os_time_get() - start_time
>= timeout
)
546 virgl_vtest_resource_wait(vws
, res
);
550 static void virgl_fence_reference(struct virgl_winsys
*vws
,
551 struct pipe_fence_handle
**dst
,
552 struct pipe_fence_handle
*src
)
554 struct virgl_vtest_winsys
*vdws
= virgl_vtest_winsys(vws
);
555 virgl_vtest_resource_reference(vdws
, (struct virgl_hw_res
**)dst
,
559 static void virgl_vtest_flush_frontbuffer(struct virgl_winsys
*vws
,
560 struct virgl_hw_res
*res
,
561 unsigned level
, unsigned layer
,
562 void *winsys_drawable_handle
,
563 struct pipe_box
*sub_box
)
565 struct virgl_vtest_winsys
*vtws
= virgl_vtest_winsys(vws
);
569 uint32_t offset
= 0, valid_stride
;
573 memset(&box
, 0, sizeof(box
));
577 offset
= (res
->stride
* (box
.y
/ util_format_get_blockheight(res
->format
))) + (box
.x
/ util_format_get_blockwidth(res
->format
)) * util_format_get_blocksize(res
->format
);
580 box
.width
= res
->width
;
581 box
.height
= res
->height
;
585 size
= vtest_get_transfer_size(res
, &box
, res
->stride
, 0, level
, &valid_stride
);
587 virgl_vtest_busy_wait(vtws
, res
->res_handle
, VCMD_BUSY_WAIT_FLAG_WAIT
);
588 map
= vtws
->sws
->displaytarget_map(vtws
->sws
, res
->dt
, 0);
590 /* execute a transfer */
591 virgl_vtest_send_transfer_cmd(vtws
, VCMD_TRANSFER_GET
, res
->res_handle
,
592 level
, res
->stride
, 0, &box
, size
);
593 virgl_vtest_recv_transfer_get_data(vtws
, map
+ offset
, size
, valid_stride
, &box
, res
->format
);
594 vtws
->sws
->displaytarget_unmap(vtws
->sws
, res
->dt
);
596 vtws
->sws
->displaytarget_display(vtws
->sws
, res
->dt
, winsys_drawable_handle
, sub_box
);
600 virgl_vtest_winsys_destroy(struct virgl_winsys
*vws
)
602 struct virgl_vtest_winsys
*vtws
= virgl_vtest_winsys(vws
);
604 virgl_cache_flush(vtws
);
606 pipe_mutex_destroy(vtws
->mutex
);
610 struct virgl_winsys
*
611 virgl_vtest_winsys_wrap(struct sw_winsys
*sws
)
613 struct virgl_vtest_winsys
*vtws
;
615 vtws
= CALLOC_STRUCT(virgl_vtest_winsys
);
619 virgl_vtest_connect(vtws
);
622 vtws
->usecs
= 1000000;
623 LIST_INITHEAD(&vtws
->delayed
);
624 pipe_mutex_init(vtws
->mutex
);
626 vtws
->base
.destroy
= virgl_vtest_winsys_destroy
;
628 vtws
->base
.transfer_put
= virgl_vtest_transfer_put
;
629 vtws
->base
.transfer_get
= virgl_vtest_transfer_get
;
631 vtws
->base
.resource_create
= virgl_vtest_winsys_resource_cache_create
;
632 vtws
->base
.resource_unref
= virgl_vtest_winsys_resource_unref
;
633 vtws
->base
.resource_map
= virgl_vtest_resource_map
;
634 vtws
->base
.resource_wait
= virgl_vtest_resource_wait
;
635 vtws
->base
.cmd_buf_create
= virgl_vtest_cmd_buf_create
;
636 vtws
->base
.cmd_buf_destroy
= virgl_vtest_cmd_buf_destroy
;
637 vtws
->base
.submit_cmd
= virgl_vtest_winsys_submit_cmd
;
639 vtws
->base
.emit_res
= virgl_vtest_emit_res
;
640 vtws
->base
.res_is_referenced
= virgl_vtest_res_is_ref
;
641 vtws
->base
.get_caps
= virgl_vtest_get_caps
;
643 vtws
->base
.cs_create_fence
= virgl_cs_create_fence
;
644 vtws
->base
.fence_wait
= virgl_fence_wait
;
645 vtws
->base
.fence_reference
= virgl_fence_reference
;
647 vtws
->base
.flush_frontbuffer
= virgl_vtest_flush_frontbuffer
;