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
, struct virgl_hw_res
*res
);
34 static void virgl_vtest_resource_unmap(struct virgl_winsys
*vws
, struct virgl_hw_res
*res
);
35 static inline boolean
can_cache_resource(struct virgl_hw_res
*res
)
37 return res
->cacheable
== TRUE
;
40 static uint32_t vtest_get_transfer_size(struct virgl_hw_res
*res
,
41 const struct pipe_box
*box
,
42 uint32_t stride
, uint32_t layer_stride
,
43 uint32_t level
, uint32_t *valid_stride_p
)
45 uint32_t valid_stride
, valid_layer_stride
;
47 valid_stride
= util_format_get_stride(res
->format
, box
->width
);
50 valid_stride
= stride
;
53 valid_layer_stride
= util_format_get_2d_size(res
->format
, valid_stride
,
57 valid_layer_stride
= layer_stride
;
60 *valid_stride_p
= valid_stride
;
61 return valid_layer_stride
* box
->depth
;
65 virgl_vtest_transfer_put(struct virgl_winsys
*vws
,
66 struct virgl_hw_res
*res
,
67 const struct pipe_box
*box
,
68 uint32_t stride
, uint32_t layer_stride
,
69 uint32_t buf_offset
, uint32_t level
)
71 struct virgl_vtest_winsys
*vtws
= virgl_vtest_winsys(vws
);
74 uint32_t valid_stride
;
76 size
= vtest_get_transfer_size(res
, box
, stride
, layer_stride
, level
, &valid_stride
);
78 virgl_vtest_send_transfer_cmd(vtws
, VCMD_TRANSFER_PUT
, res
->res_handle
,
79 level
, stride
, layer_stride
,
81 ptr
= virgl_vtest_resource_map(vws
, res
);
82 virgl_vtest_send_transfer_put_data(vtws
, ptr
+ buf_offset
, size
);
83 virgl_vtest_resource_unmap(vws
, res
);
88 virgl_vtest_transfer_get(struct virgl_winsys
*vws
,
89 struct virgl_hw_res
*res
,
90 const struct pipe_box
*box
,
91 uint32_t stride
, uint32_t layer_stride
,
92 uint32_t buf_offset
, uint32_t level
)
94 struct virgl_vtest_winsys
*vtws
= virgl_vtest_winsys(vws
);
97 uint32_t valid_stride
;
99 size
= vtest_get_transfer_size(res
, box
, stride
, layer_stride
, level
, &valid_stride
);
101 virgl_vtest_send_transfer_cmd(vtws
, VCMD_TRANSFER_GET
, res
->res_handle
,
102 level
, stride
, layer_stride
,
106 ptr
= virgl_vtest_resource_map(vws
, res
);
107 virgl_vtest_recv_transfer_get_data(vtws
, ptr
+ buf_offset
, size
, valid_stride
, box
, res
->format
);
108 virgl_vtest_resource_unmap(vws
, res
);
112 static void virgl_hw_res_destroy(struct virgl_vtest_winsys
*vtws
,
113 struct virgl_hw_res
*res
)
115 virgl_vtest_send_resource_unref(vtws
, res
->res_handle
);
117 vtws
->sws
->displaytarget_destroy(vtws
->sws
, res
->dt
);
122 static boolean
virgl_vtest_resource_is_busy(struct virgl_vtest_winsys
*vtws
,
123 struct virgl_hw_res
*res
)
125 /* implement busy check */
127 ret
= virgl_vtest_busy_wait(vtws
, res
->res_handle
, 0);
132 return ret
== 1 ? TRUE
: FALSE
;
136 virgl_cache_flush(struct virgl_vtest_winsys
*vtws
)
138 struct list_head
*curr
, *next
;
139 struct virgl_hw_res
*res
;
141 pipe_mutex_lock(vtws
->mutex
);
142 curr
= vtws
->delayed
.next
;
145 while (curr
!= &vtws
->delayed
) {
146 res
= LIST_ENTRY(struct virgl_hw_res
, curr
, head
);
147 LIST_DEL(&res
->head
);
148 virgl_hw_res_destroy(vtws
, res
);
152 pipe_mutex_unlock(vtws
->mutex
);
156 virgl_cache_list_check_free(struct virgl_vtest_winsys
*vtws
)
158 struct list_head
*curr
, *next
;
159 struct virgl_hw_res
*res
;
163 curr
= vtws
->delayed
.next
;
165 while (curr
!= &vtws
->delayed
) {
166 res
= LIST_ENTRY(struct virgl_hw_res
, curr
, head
);
167 if (!os_time_timeout(res
->start
, res
->end
, now
))
170 LIST_DEL(&res
->head
);
171 virgl_hw_res_destroy(vtws
, res
);
177 static void virgl_vtest_resource_reference(struct virgl_vtest_winsys
*vtws
,
178 struct virgl_hw_res
**dres
,
179 struct virgl_hw_res
*sres
)
181 struct virgl_hw_res
*old
= *dres
;
182 if (pipe_reference(&(*dres
)->reference
, &sres
->reference
)) {
183 if (!can_cache_resource(old
)) {
184 virgl_hw_res_destroy(vtws
, old
);
186 pipe_mutex_lock(vtws
->mutex
);
187 virgl_cache_list_check_free(vtws
);
189 old
->start
= os_time_get();
190 old
->end
= old
->start
+ vtws
->usecs
;
191 LIST_ADDTAIL(&old
->head
, &vtws
->delayed
);
193 pipe_mutex_unlock(vtws
->mutex
);
199 static struct virgl_hw_res
*virgl_vtest_winsys_resource_create(
200 struct virgl_winsys
*vws
,
201 enum pipe_texture_target target
,
212 struct virgl_vtest_winsys
*vtws
= virgl_vtest_winsys(vws
);
213 struct virgl_hw_res
*res
;
214 static int handle
= 1;
216 res
= CALLOC_STRUCT(virgl_hw_res
);
220 if (bind
& (VIRGL_BIND_DISPLAY_TARGET
| VIRGL_BIND_SCANOUT
)) {
221 res
->dt
= vtws
->sws
->displaytarget_create(vtws
->sws
,
230 res
->ptr
= align_malloc(size
, 64);
238 res
->format
= format
;
239 res
->height
= height
;
241 virgl_vtest_send_resource_create(vtws
, handle
, target
, format
, bind
, width
,
242 height
, depth
, array_size
, last_level
,
245 res
->res_handle
= handle
++;
246 pipe_reference_init(&res
->reference
, 1);
250 static void virgl_vtest_winsys_resource_unref(struct virgl_winsys
*vws
,
251 struct virgl_hw_res
*hres
)
253 struct virgl_vtest_winsys
*vtws
= virgl_vtest_winsys(vws
);
254 virgl_vtest_resource_reference(vtws
, &hres
, NULL
);
257 static void *virgl_vtest_resource_map(struct virgl_winsys
*vws
, struct virgl_hw_res
*res
)
259 struct virgl_vtest_winsys
*vtws
= virgl_vtest_winsys(vws
);
262 return vtws
->sws
->displaytarget_map(vtws
->sws
, res
->dt
, 0);
264 res
->mapped
= res
->ptr
;
269 static void virgl_vtest_resource_unmap(struct virgl_winsys
*vws
, struct virgl_hw_res
*res
)
271 struct virgl_vtest_winsys
*vtws
= virgl_vtest_winsys(vws
);
276 vtws
->sws
->displaytarget_unmap(vtws
->sws
, res
->dt
);
279 static void virgl_vtest_resource_wait(struct virgl_winsys
*vws
, struct virgl_hw_res
*res
)
281 struct virgl_vtest_winsys
*vtws
= virgl_vtest_winsys(vws
);
283 virgl_vtest_busy_wait(vtws
, res
->res_handle
, VCMD_BUSY_WAIT_FLAG_WAIT
);
286 static inline int virgl_is_res_compat(struct virgl_vtest_winsys
*vtws
,
287 struct virgl_hw_res
*res
,
288 uint32_t size
, uint32_t bind
, uint32_t format
)
290 if (res
->bind
!= bind
)
292 if (res
->format
!= format
)
294 if (res
->size
< size
)
296 if (res
->size
> size
* 2)
299 if (virgl_vtest_resource_is_busy(vtws
, res
)) {
306 static struct virgl_hw_res
*virgl_vtest_winsys_resource_cache_create(struct virgl_winsys
*vws
,
307 enum pipe_texture_target target
,
318 struct virgl_vtest_winsys
*vtws
= virgl_vtest_winsys(vws
);
319 struct virgl_hw_res
*res
, *curr_res
;
320 struct list_head
*curr
, *next
;
324 /* only store binds for vertex/index/const buffers */
325 if (bind
!= VIRGL_BIND_CONSTANT_BUFFER
&& bind
!= VIRGL_BIND_INDEX_BUFFER
&&
326 bind
!= VIRGL_BIND_VERTEX_BUFFER
&& bind
!= VIRGL_BIND_CUSTOM
)
329 pipe_mutex_lock(vtws
->mutex
);
332 curr
= vtws
->delayed
.next
;
336 while (curr
!= &vtws
->delayed
) {
337 curr_res
= LIST_ENTRY(struct virgl_hw_res
, curr
, head
);
339 if (!res
&& (ret
= virgl_is_res_compat(vtws
, curr_res
, size
, bind
, format
) > 0))
341 else if (os_time_timeout(curr_res
->start
, curr_res
->end
, now
)) {
342 LIST_DEL(&curr_res
->head
);
343 virgl_hw_res_destroy(vtws
, curr_res
);
354 if (!res
&& ret
!= -1) {
355 while (curr
!= &vtws
->delayed
) {
356 curr_res
= LIST_ENTRY(struct virgl_hw_res
, curr
, head
);
357 ret
= virgl_is_res_compat(vtws
, curr_res
, size
, bind
, format
);
370 LIST_DEL(&res
->head
);
372 pipe_mutex_unlock(vtws
->mutex
);
373 pipe_reference_init(&res
->reference
, 1);
377 pipe_mutex_unlock(vtws
->mutex
);
380 res
= virgl_vtest_winsys_resource_create(vws
, target
, format
, bind
,
381 width
, height
, depth
, array_size
,
382 last_level
, nr_samples
, size
);
383 if (bind
== VIRGL_BIND_CONSTANT_BUFFER
|| bind
== VIRGL_BIND_INDEX_BUFFER
||
384 bind
== VIRGL_BIND_VERTEX_BUFFER
)
385 res
->cacheable
= TRUE
;
389 static struct virgl_cmd_buf
*virgl_vtest_cmd_buf_create(struct virgl_winsys
*vws
)
391 struct virgl_vtest_cmd_buf
*cbuf
;
393 cbuf
= CALLOC_STRUCT(virgl_vtest_cmd_buf
);
398 cbuf
->res_bo
= CALLOC(cbuf
->nres
, sizeof(struct virgl_hw_buf
*));
404 cbuf
->base
.buf
= cbuf
->buf
;
408 static void virgl_vtest_cmd_buf_destroy(struct virgl_cmd_buf
*_cbuf
)
410 struct virgl_vtest_cmd_buf
*cbuf
= virgl_vtest_cmd_buf(_cbuf
);
416 static boolean
virgl_vtest_lookup_res(struct virgl_vtest_cmd_buf
*cbuf
,
417 struct virgl_hw_res
*res
)
419 unsigned hash
= res
->res_handle
& (sizeof(cbuf
->is_handle_added
)-1);
422 if (cbuf
->is_handle_added
[hash
]) {
423 i
= cbuf
->reloc_indices_hashlist
[hash
];
424 if (cbuf
->res_bo
[i
] == res
)
427 for (i
= 0; i
< cbuf
->cres
; i
++) {
428 if (cbuf
->res_bo
[i
] == res
) {
429 cbuf
->reloc_indices_hashlist
[hash
] = i
;
437 static void virgl_vtest_release_all_res(struct virgl_vtest_winsys
*vtws
,
438 struct virgl_vtest_cmd_buf
*cbuf
)
442 for (i
= 0; i
< cbuf
->cres
; i
++) {
443 p_atomic_dec(&cbuf
->res_bo
[i
]->num_cs_references
);
444 virgl_vtest_resource_reference(vtws
, &cbuf
->res_bo
[i
], NULL
);
449 static void virgl_vtest_add_res(struct virgl_vtest_winsys
*vtws
,
450 struct virgl_vtest_cmd_buf
*cbuf
, struct virgl_hw_res
*res
)
452 unsigned hash
= res
->res_handle
& (sizeof(cbuf
->is_handle_added
)-1);
454 if (cbuf
->cres
> cbuf
->nres
) {
455 fprintf(stderr
,"failure to add relocation\n");
459 cbuf
->res_bo
[cbuf
->cres
] = NULL
;
460 virgl_vtest_resource_reference(vtws
, &cbuf
->res_bo
[cbuf
->cres
], res
);
461 cbuf
->is_handle_added
[hash
] = TRUE
;
463 cbuf
->reloc_indices_hashlist
[hash
] = cbuf
->cres
;
464 p_atomic_inc(&res
->num_cs_references
);
468 static int virgl_vtest_winsys_submit_cmd(struct virgl_winsys
*vws
, struct virgl_cmd_buf
*_cbuf
)
470 struct virgl_vtest_winsys
*vtws
= virgl_vtest_winsys(vws
);
471 struct virgl_vtest_cmd_buf
*cbuf
= virgl_vtest_cmd_buf(_cbuf
);
474 if (cbuf
->base
.cdw
== 0)
477 ret
= virgl_vtest_submit_cmd(vtws
, cbuf
);
479 virgl_vtest_release_all_res(vtws
, cbuf
);
480 memset(cbuf
->is_handle_added
, 0, sizeof(cbuf
->is_handle_added
));
485 static void virgl_vtest_emit_res(struct virgl_winsys
*vws
, struct virgl_cmd_buf
*_cbuf
, struct virgl_hw_res
*res
, boolean write_buf
)
487 struct virgl_vtest_winsys
*vtws
= virgl_vtest_winsys(vws
);
488 struct virgl_vtest_cmd_buf
*cbuf
= virgl_vtest_cmd_buf(_cbuf
);
489 boolean already_in_list
= virgl_vtest_lookup_res(cbuf
, res
);
492 cbuf
->base
.buf
[cbuf
->base
.cdw
++] = res
->res_handle
;
493 if (!already_in_list
)
494 virgl_vtest_add_res(vtws
, cbuf
, res
);
497 static boolean
virgl_vtest_res_is_ref(struct virgl_winsys
*vws
,
498 struct virgl_cmd_buf
*_cbuf
,
499 struct virgl_hw_res
*res
)
501 if (!res
->num_cs_references
)
507 static int virgl_vtest_get_caps(struct virgl_winsys
*vws
, struct virgl_drm_caps
*caps
)
509 struct virgl_vtest_winsys
*vtws
= virgl_vtest_winsys(vws
);
510 return virgl_vtest_send_get_caps(vtws
, caps
);
513 static struct pipe_fence_handle
*
514 virgl_cs_create_fence(struct virgl_winsys
*vws
)
516 struct virgl_hw_res
*res
;
518 res
= virgl_vtest_winsys_resource_cache_create(vws
,
520 PIPE_FORMAT_R8_UNORM
,
522 8, 1, 1, 0, 0, 0, 8);
524 return (struct pipe_fence_handle
*)res
;
527 static bool virgl_fence_wait(struct virgl_winsys
*vws
,
528 struct pipe_fence_handle
*fence
,
531 struct virgl_vtest_winsys
*vdws
= virgl_vtest_winsys(vws
);
532 struct virgl_hw_res
*res
= virgl_hw_res(fence
);
535 return virgl_vtest_resource_is_busy(vdws
, res
);
537 if (timeout
!= PIPE_TIMEOUT_INFINITE
) {
538 int64_t start_time
= os_time_get();
540 while (virgl_vtest_resource_is_busy(vdws
, res
)) {
541 if (os_time_get() - start_time
>= timeout
)
547 virgl_vtest_resource_wait(vws
, res
);
551 static void virgl_fence_reference(struct virgl_winsys
*vws
,
552 struct pipe_fence_handle
**dst
,
553 struct pipe_fence_handle
*src
)
555 struct virgl_vtest_winsys
*vdws
= virgl_vtest_winsys(vws
);
556 virgl_vtest_resource_reference(vdws
, (struct virgl_hw_res
**)dst
,
560 static void virgl_vtest_flush_frontbuffer(struct virgl_winsys
*vws
,
561 struct virgl_hw_res
*res
,
562 unsigned level
, unsigned layer
,
563 void *winsys_drawable_handle
,
564 struct pipe_box
*sub_box
)
566 struct virgl_vtest_winsys
*vtws
= virgl_vtest_winsys(vws
);
570 uint32_t offset
= 0, valid_stride
;
574 memset(&box
, 0, sizeof(box
));
578 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
);
581 box
.width
= res
->width
;
582 box
.height
= res
->height
;
586 size
= vtest_get_transfer_size(res
, &box
, res
->stride
, 0, level
, &valid_stride
);
588 virgl_vtest_busy_wait(vtws
, res
->res_handle
, VCMD_BUSY_WAIT_FLAG_WAIT
);
589 map
= vtws
->sws
->displaytarget_map(vtws
->sws
, res
->dt
, 0);
591 /* execute a transfer */
592 virgl_vtest_send_transfer_cmd(vtws
, VCMD_TRANSFER_GET
, res
->res_handle
,
593 level
, res
->stride
, 0, &box
, size
);
594 virgl_vtest_recv_transfer_get_data(vtws
, map
+ offset
, size
, valid_stride
, &box
, res
->format
);
595 vtws
->sws
->displaytarget_unmap(vtws
->sws
, res
->dt
);
597 vtws
->sws
->displaytarget_display(vtws
->sws
, res
->dt
, winsys_drawable_handle
, sub_box
);
601 virgl_vtest_winsys_destroy(struct virgl_winsys
*vws
)
603 struct virgl_vtest_winsys
*vtws
= virgl_vtest_winsys(vws
);
605 virgl_cache_flush(vtws
);
607 pipe_mutex_destroy(vtws
->mutex
);
611 struct virgl_winsys
*
612 virgl_vtest_winsys_wrap(struct sw_winsys
*sws
)
614 struct virgl_vtest_winsys
*vtws
;
616 vtws
= CALLOC_STRUCT(virgl_vtest_winsys
);
620 virgl_vtest_connect(vtws
);
623 vtws
->usecs
= 1000000;
624 LIST_INITHEAD(&vtws
->delayed
);
625 pipe_mutex_init(vtws
->mutex
);
627 vtws
->base
.destroy
= virgl_vtest_winsys_destroy
;
629 vtws
->base
.transfer_put
= virgl_vtest_transfer_put
;
630 vtws
->base
.transfer_get
= virgl_vtest_transfer_get
;
632 vtws
->base
.resource_create
= virgl_vtest_winsys_resource_cache_create
;
633 vtws
->base
.resource_unref
= virgl_vtest_winsys_resource_unref
;
634 vtws
->base
.resource_map
= virgl_vtest_resource_map
;
635 vtws
->base
.resource_wait
= virgl_vtest_resource_wait
;
636 vtws
->base
.cmd_buf_create
= virgl_vtest_cmd_buf_create
;
637 vtws
->base
.cmd_buf_destroy
= virgl_vtest_cmd_buf_destroy
;
638 vtws
->base
.submit_cmd
= virgl_vtest_winsys_submit_cmd
;
640 vtws
->base
.emit_res
= virgl_vtest_emit_res
;
641 vtws
->base
.res_is_referenced
= virgl_vtest_res_is_ref
;
642 vtws
->base
.get_caps
= virgl_vtest_get_caps
;
644 vtws
->base
.cs_create_fence
= virgl_cs_create_fence
;
645 vtws
->base
.fence_wait
= virgl_fence_wait
;
646 vtws
->base
.fence_reference
= virgl_fence_reference
;
648 vtws
->base
.flush_frontbuffer
= virgl_vtest_flush_frontbuffer
;