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_surface.h"
25 #include "util/u_memory.h"
26 #include "util/format/u_format.h"
27 #include "util/u_inlines.h"
28 #include "util/os_time.h"
29 #include "state_tracker/sw_winsys.h"
30 #include "os/os_mman.h"
32 #include "virgl_vtest_winsys.h"
33 #include "virgl_vtest_public.h"
35 /* Gets a pointer to the virgl_hw_res containing the pointed to cache entry. */
36 #define cache_entry_container_res(ptr) \
37 (struct virgl_hw_res*)((char*)ptr - offsetof(struct virgl_hw_res, cache_entry))
39 static void *virgl_vtest_resource_map(struct virgl_winsys
*vws
,
40 struct virgl_hw_res
*res
);
41 static void virgl_vtest_resource_unmap(struct virgl_winsys
*vws
,
42 struct virgl_hw_res
*res
);
44 static inline boolean
can_cache_resource_with_bind(uint32_t bind
)
46 return bind
== VIRGL_BIND_CONSTANT_BUFFER
||
47 bind
== VIRGL_BIND_INDEX_BUFFER
||
48 bind
== VIRGL_BIND_VERTEX_BUFFER
||
49 bind
== VIRGL_BIND_CUSTOM
||
50 bind
== VIRGL_BIND_STAGING
;
53 static uint32_t vtest_get_transfer_size(struct virgl_hw_res
*res
,
54 const struct pipe_box
*box
,
55 uint32_t stride
, uint32_t layer_stride
,
56 uint32_t level
, uint32_t *valid_stride_p
)
58 uint32_t valid_stride
, valid_layer_stride
;
60 valid_stride
= util_format_get_stride(res
->format
, box
->width
);
63 valid_stride
= stride
;
66 valid_layer_stride
= util_format_get_2d_size(res
->format
, valid_stride
,
70 valid_layer_stride
= layer_stride
;
73 *valid_stride_p
= valid_stride
;
74 return valid_layer_stride
* box
->depth
;
78 virgl_vtest_transfer_put(struct virgl_winsys
*vws
,
79 struct virgl_hw_res
*res
,
80 const struct pipe_box
*box
,
81 uint32_t stride
, uint32_t layer_stride
,
82 uint32_t buf_offset
, uint32_t level
)
84 struct virgl_vtest_winsys
*vtws
= virgl_vtest_winsys(vws
);
87 uint32_t valid_stride
;
89 size
= vtest_get_transfer_size(res
, box
, stride
, layer_stride
, level
,
92 virgl_vtest_send_transfer_put(vtws
, res
->res_handle
,
93 level
, stride
, layer_stride
,
94 box
, size
, buf_offset
);
96 if (vtws
->protocol_version
>= 2)
99 ptr
= virgl_vtest_resource_map(vws
, res
);
100 virgl_vtest_send_transfer_put_data(vtws
, ptr
+ buf_offset
, size
);
101 virgl_vtest_resource_unmap(vws
, res
);
106 virgl_vtest_transfer_get_internal(struct virgl_winsys
*vws
,
107 struct virgl_hw_res
*res
,
108 const struct pipe_box
*box
,
109 uint32_t stride
, uint32_t layer_stride
,
110 uint32_t buf_offset
, uint32_t level
,
111 bool flush_front_buffer
)
113 struct virgl_vtest_winsys
*vtws
= virgl_vtest_winsys(vws
);
116 uint32_t valid_stride
;
118 size
= vtest_get_transfer_size(res
, box
, stride
, layer_stride
, level
,
120 virgl_vtest_send_transfer_get(vtws
, res
->res_handle
,
121 level
, stride
, layer_stride
,
122 box
, size
, buf_offset
);
124 if (flush_front_buffer
|| vtws
->protocol_version
>= 2)
125 virgl_vtest_busy_wait(vtws
, res
->res_handle
, VCMD_BUSY_WAIT_FLAG_WAIT
);
127 if (vtws
->protocol_version
>= 2) {
128 if (flush_front_buffer
) {
129 if (box
->depth
> 1 || box
->z
> 1) {
130 fprintf(stderr
, "Expected a 2D resource, received a 3D resource\n");
138 * The display target is aligned to 64 bytes, while the shared resource
139 * between the client/server is not.
141 shm_stride
= util_format_get_stride(res
->format
, res
->width
);
142 ptr
= virgl_vtest_resource_map(vws
, res
);
143 dt_map
= vtws
->sws
->displaytarget_map(vtws
->sws
, res
->dt
, 0);
145 util_copy_rect(dt_map
, res
->format
, res
->stride
, box
->x
, box
->y
,
146 box
->width
, box
->height
, ptr
, shm_stride
, box
->x
,
149 virgl_vtest_resource_unmap(vws
, res
);
150 vtws
->sws
->displaytarget_unmap(vtws
->sws
, res
->dt
);
153 ptr
= virgl_vtest_resource_map(vws
, res
);
154 virgl_vtest_recv_transfer_get_data(vtws
, ptr
+ buf_offset
, size
,
155 valid_stride
, box
, res
->format
);
156 virgl_vtest_resource_unmap(vws
, res
);
163 virgl_vtest_transfer_get(struct virgl_winsys
*vws
,
164 struct virgl_hw_res
*res
,
165 const struct pipe_box
*box
,
166 uint32_t stride
, uint32_t layer_stride
,
167 uint32_t buf_offset
, uint32_t level
)
169 return virgl_vtest_transfer_get_internal(vws
, res
, box
, stride
,
170 layer_stride
, buf_offset
,
174 static void virgl_hw_res_destroy(struct virgl_vtest_winsys
*vtws
,
175 struct virgl_hw_res
*res
)
177 virgl_vtest_send_resource_unref(vtws
, res
->res_handle
);
179 vtws
->sws
->displaytarget_destroy(vtws
->sws
, res
->dt
);
180 if (vtws
->protocol_version
>= 2) {
182 os_munmap(res
->ptr
, res
->size
);
190 static boolean
virgl_vtest_resource_is_busy(struct virgl_winsys
*vws
,
191 struct virgl_hw_res
*res
)
193 struct virgl_vtest_winsys
*vtws
= virgl_vtest_winsys(vws
);
195 /* implement busy check */
197 ret
= virgl_vtest_busy_wait(vtws
, res
->res_handle
, 0);
202 return ret
== 1 ? TRUE
: FALSE
;
205 static void virgl_vtest_resource_reference(struct virgl_winsys
*vws
,
206 struct virgl_hw_res
**dres
,
207 struct virgl_hw_res
*sres
)
209 struct virgl_vtest_winsys
*vtws
= virgl_vtest_winsys(vws
);
210 struct virgl_hw_res
*old
= *dres
;
212 if (pipe_reference(&(*dres
)->reference
, &sres
->reference
)) {
213 if (!can_cache_resource_with_bind(old
->bind
)) {
214 virgl_hw_res_destroy(vtws
, old
);
216 mtx_lock(&vtws
->mutex
);
217 virgl_resource_cache_add(&vtws
->cache
, &old
->cache_entry
);
218 mtx_unlock(&vtws
->mutex
);
224 static struct virgl_hw_res
*
225 virgl_vtest_winsys_resource_create(struct virgl_winsys
*vws
,
226 enum pipe_texture_target target
,
237 struct virgl_vtest_winsys
*vtws
= virgl_vtest_winsys(vws
);
238 struct virgl_hw_res
*res
;
239 static int handle
= 1;
242 res
= CALLOC_STRUCT(virgl_hw_res
);
246 if (bind
& (VIRGL_BIND_DISPLAY_TARGET
| VIRGL_BIND_SCANOUT
)) {
247 res
->dt
= vtws
->sws
->displaytarget_create(vtws
->sws
, bind
, format
,
248 width
, height
, 64, NULL
,
251 } else if (vtws
->protocol_version
< 2) {
252 res
->ptr
= align_malloc(size
, 64);
260 res
->format
= format
;
261 res
->height
= height
;
264 virgl_vtest_send_resource_create(vtws
, handle
, target
, pipe_to_virgl_format(format
), bind
,
265 width
, height
, depth
, array_size
,
266 last_level
, nr_samples
, size
, &fd
);
268 if (vtws
->protocol_version
>= 2) {
269 if (res
->size
== 0) {
276 fprintf(stderr
, "Unable to get a valid fd\n");
280 res
->ptr
= os_mmap(NULL
, res
->size
, PROT_WRITE
| PROT_READ
, MAP_SHARED
,
283 if (res
->ptr
== MAP_FAILED
) {
284 fprintf(stderr
, "Client failed to map shared memory region\n");
294 virgl_resource_cache_entry_init(&res
->cache_entry
, size
, bind
, format
);
295 res
->res_handle
= handle
++;
296 pipe_reference_init(&res
->reference
, 1);
297 p_atomic_set(&res
->num_cs_references
, 0);
301 static void *virgl_vtest_resource_map(struct virgl_winsys
*vws
,
302 struct virgl_hw_res
*res
)
304 struct virgl_vtest_winsys
*vtws
= virgl_vtest_winsys(vws
);
307 * With protocol v0 we can either have a display target or a resource backing
308 * store. With protocol v2 we can have both, so only return the memory mapped
309 * backing store in this function. We can copy to the display target when
312 if (vtws
->protocol_version
>= 2 || !res
->dt
) {
313 res
->mapped
= res
->ptr
;
316 return vtws
->sws
->displaytarget_map(vtws
->sws
, res
->dt
, 0);
320 static void virgl_vtest_resource_unmap(struct virgl_winsys
*vws
,
321 struct virgl_hw_res
*res
)
323 struct virgl_vtest_winsys
*vtws
= virgl_vtest_winsys(vws
);
327 if (res
->dt
&& vtws
->protocol_version
< 2)
328 vtws
->sws
->displaytarget_unmap(vtws
->sws
, res
->dt
);
331 static void virgl_vtest_resource_wait(struct virgl_winsys
*vws
,
332 struct virgl_hw_res
*res
)
334 struct virgl_vtest_winsys
*vtws
= virgl_vtest_winsys(vws
);
336 virgl_vtest_busy_wait(vtws
, res
->res_handle
, VCMD_BUSY_WAIT_FLAG_WAIT
);
339 static struct virgl_hw_res
*
340 virgl_vtest_winsys_resource_cache_create(struct virgl_winsys
*vws
,
341 enum pipe_texture_target target
,
352 struct virgl_vtest_winsys
*vtws
= virgl_vtest_winsys(vws
);
353 struct virgl_hw_res
*res
;
354 struct virgl_resource_cache_entry
*entry
;
356 if (!can_cache_resource_with_bind(bind
))
359 mtx_lock(&vtws
->mutex
);
361 entry
= virgl_resource_cache_remove_compatible(&vtws
->cache
, size
,
364 res
= cache_entry_container_res(entry
);
365 mtx_unlock(&vtws
->mutex
);
366 pipe_reference_init(&res
->reference
, 1);
370 mtx_unlock(&vtws
->mutex
);
373 res
= virgl_vtest_winsys_resource_create(vws
, target
, format
, bind
,
374 width
, height
, depth
, array_size
,
375 last_level
, nr_samples
, size
);
379 static boolean
virgl_vtest_lookup_res(struct virgl_vtest_cmd_buf
*cbuf
,
380 struct virgl_hw_res
*res
)
382 unsigned hash
= res
->res_handle
& (sizeof(cbuf
->is_handle_added
)-1);
385 if (cbuf
->is_handle_added
[hash
]) {
386 i
= cbuf
->reloc_indices_hashlist
[hash
];
387 if (cbuf
->res_bo
[i
] == res
)
390 for (i
= 0; i
< cbuf
->cres
; i
++) {
391 if (cbuf
->res_bo
[i
] == res
) {
392 cbuf
->reloc_indices_hashlist
[hash
] = i
;
400 static void virgl_vtest_release_all_res(struct virgl_vtest_winsys
*vtws
,
401 struct virgl_vtest_cmd_buf
*cbuf
)
405 for (i
= 0; i
< cbuf
->cres
; i
++) {
406 p_atomic_dec(&cbuf
->res_bo
[i
]->num_cs_references
);
407 virgl_vtest_resource_reference(&vtws
->base
, &cbuf
->res_bo
[i
], NULL
);
412 static void virgl_vtest_add_res(struct virgl_vtest_winsys
*vtws
,
413 struct virgl_vtest_cmd_buf
*cbuf
,
414 struct virgl_hw_res
*res
)
416 unsigned hash
= res
->res_handle
& (sizeof(cbuf
->is_handle_added
)-1);
418 if (cbuf
->cres
>= cbuf
->nres
) {
419 unsigned new_nres
= cbuf
->nres
+ 256;
420 struct virgl_hw_res
**new_re_bo
= REALLOC(cbuf
->res_bo
,
421 cbuf
->nres
* sizeof(struct virgl_hw_buf
*),
422 new_nres
* sizeof(struct virgl_hw_buf
*));
424 fprintf(stderr
,"failure to add relocation %d, %d\n", cbuf
->cres
, cbuf
->nres
);
428 cbuf
->res_bo
= new_re_bo
;
429 cbuf
->nres
= new_nres
;
432 cbuf
->res_bo
[cbuf
->cres
] = NULL
;
433 virgl_vtest_resource_reference(&vtws
->base
, &cbuf
->res_bo
[cbuf
->cres
], res
);
434 cbuf
->is_handle_added
[hash
] = TRUE
;
436 cbuf
->reloc_indices_hashlist
[hash
] = cbuf
->cres
;
437 p_atomic_inc(&res
->num_cs_references
);
441 static struct virgl_cmd_buf
*virgl_vtest_cmd_buf_create(struct virgl_winsys
*vws
,
444 struct virgl_vtest_cmd_buf
*cbuf
;
446 cbuf
= CALLOC_STRUCT(virgl_vtest_cmd_buf
);
451 cbuf
->res_bo
= CALLOC(cbuf
->nres
, sizeof(struct virgl_hw_buf
*));
457 cbuf
->buf
= CALLOC(size
, sizeof(uint32_t));
465 cbuf
->base
.buf
= cbuf
->buf
;
469 static void virgl_vtest_cmd_buf_destroy(struct virgl_cmd_buf
*_cbuf
)
471 struct virgl_vtest_cmd_buf
*cbuf
= virgl_vtest_cmd_buf(_cbuf
);
473 virgl_vtest_release_all_res(virgl_vtest_winsys(cbuf
->ws
), cbuf
);
479 static struct pipe_fence_handle
*
480 virgl_vtest_fence_create(struct virgl_winsys
*vws
)
482 struct virgl_hw_res
*res
;
484 /* Resources for fences should not be from the cache, since we are basing
485 * the fence status on the resource creation busy status.
487 res
= virgl_vtest_winsys_resource_create(vws
,
489 PIPE_FORMAT_R8_UNORM
,
491 8, 1, 1, 0, 0, 0, 8);
493 return (struct pipe_fence_handle
*)res
;
496 static int virgl_vtest_winsys_submit_cmd(struct virgl_winsys
*vws
,
497 struct virgl_cmd_buf
*_cbuf
,
498 struct pipe_fence_handle
**fence
)
500 struct virgl_vtest_winsys
*vtws
= virgl_vtest_winsys(vws
);
501 struct virgl_vtest_cmd_buf
*cbuf
= virgl_vtest_cmd_buf(_cbuf
);
504 if (cbuf
->base
.cdw
== 0)
507 ret
= virgl_vtest_submit_cmd(vtws
, cbuf
);
508 if (fence
&& ret
== 0)
509 *fence
= virgl_vtest_fence_create(vws
);
511 virgl_vtest_release_all_res(vtws
, cbuf
);
512 memset(cbuf
->is_handle_added
, 0, sizeof(cbuf
->is_handle_added
));
517 static void virgl_vtest_emit_res(struct virgl_winsys
*vws
,
518 struct virgl_cmd_buf
*_cbuf
,
519 struct virgl_hw_res
*res
, boolean write_buf
)
521 struct virgl_vtest_winsys
*vtws
= virgl_vtest_winsys(vws
);
522 struct virgl_vtest_cmd_buf
*cbuf
= virgl_vtest_cmd_buf(_cbuf
);
523 boolean already_in_list
= virgl_vtest_lookup_res(cbuf
, res
);
526 cbuf
->base
.buf
[cbuf
->base
.cdw
++] = res
->res_handle
;
527 if (!already_in_list
)
528 virgl_vtest_add_res(vtws
, cbuf
, res
);
531 static boolean
virgl_vtest_res_is_ref(struct virgl_winsys
*vws
,
532 struct virgl_cmd_buf
*_cbuf
,
533 struct virgl_hw_res
*res
)
535 if (!p_atomic_read(&res
->num_cs_references
))
541 static int virgl_vtest_get_caps(struct virgl_winsys
*vws
,
542 struct virgl_drm_caps
*caps
)
544 struct virgl_vtest_winsys
*vtws
= virgl_vtest_winsys(vws
);
546 virgl_ws_fill_new_caps_defaults(caps
);
547 return virgl_vtest_send_get_caps(vtws
, caps
);
550 static struct pipe_fence_handle
*
551 virgl_cs_create_fence(struct virgl_winsys
*vws
, int fd
)
553 return virgl_vtest_fence_create(vws
);
556 static bool virgl_fence_wait(struct virgl_winsys
*vws
,
557 struct pipe_fence_handle
*fence
,
560 struct virgl_hw_res
*res
= virgl_hw_res(fence
);
563 return !virgl_vtest_resource_is_busy(vws
, res
);
565 if (timeout
!= PIPE_TIMEOUT_INFINITE
) {
566 int64_t start_time
= os_time_get();
568 while (virgl_vtest_resource_is_busy(vws
, res
)) {
569 if (os_time_get() - start_time
>= timeout
)
575 virgl_vtest_resource_wait(vws
, res
);
579 static void virgl_fence_reference(struct virgl_winsys
*vws
,
580 struct pipe_fence_handle
**dst
,
581 struct pipe_fence_handle
*src
)
583 struct virgl_vtest_winsys
*vdws
= virgl_vtest_winsys(vws
);
584 virgl_vtest_resource_reference(&vdws
->base
, (struct virgl_hw_res
**)dst
,
588 static void virgl_vtest_flush_frontbuffer(struct virgl_winsys
*vws
,
589 struct virgl_hw_res
*res
,
590 unsigned level
, unsigned layer
,
591 void *winsys_drawable_handle
,
592 struct pipe_box
*sub_box
)
594 struct virgl_vtest_winsys
*vtws
= virgl_vtest_winsys(vws
);
600 memset(&box
, 0, sizeof(box
));
604 offset
= box
.y
/ util_format_get_blockheight(res
->format
) * res
->stride
+
605 box
.x
/ util_format_get_blockwidth(res
->format
) * util_format_get_blocksize(res
->format
);
608 box
.width
= res
->width
;
609 box
.height
= res
->height
;
613 virgl_vtest_transfer_get_internal(vws
, res
, &box
, res
->stride
, 0, offset
,
616 vtws
->sws
->displaytarget_display(vtws
->sws
, res
->dt
, winsys_drawable_handle
,
621 virgl_vtest_winsys_destroy(struct virgl_winsys
*vws
)
623 struct virgl_vtest_winsys
*vtws
= virgl_vtest_winsys(vws
);
625 virgl_resource_cache_flush(&vtws
->cache
);
627 mtx_destroy(&vtws
->mutex
);
632 virgl_vtest_resource_cache_entry_is_busy(struct virgl_resource_cache_entry
*entry
,
635 struct virgl_vtest_winsys
*vtws
= user_data
;
636 struct virgl_hw_res
*res
= cache_entry_container_res(entry
);
638 return virgl_vtest_resource_is_busy(&vtws
->base
, res
);
642 virgl_vtest_resource_cache_entry_release(struct virgl_resource_cache_entry
*entry
,
645 struct virgl_vtest_winsys
*vtws
= user_data
;
646 struct virgl_hw_res
*res
= cache_entry_container_res(entry
);
648 virgl_hw_res_destroy(vtws
, res
);
651 struct virgl_winsys
*
652 virgl_vtest_winsys_wrap(struct sw_winsys
*sws
)
654 static const unsigned CACHE_TIMEOUT_USEC
= 1000000;
655 struct virgl_vtest_winsys
*vtws
;
657 vtws
= CALLOC_STRUCT(virgl_vtest_winsys
);
661 virgl_vtest_connect(vtws
);
664 virgl_resource_cache_init(&vtws
->cache
, CACHE_TIMEOUT_USEC
,
665 virgl_vtest_resource_cache_entry_is_busy
,
666 virgl_vtest_resource_cache_entry_release
,
668 (void) mtx_init(&vtws
->mutex
, mtx_plain
);
670 vtws
->base
.destroy
= virgl_vtest_winsys_destroy
;
672 vtws
->base
.transfer_put
= virgl_vtest_transfer_put
;
673 vtws
->base
.transfer_get
= virgl_vtest_transfer_get
;
675 vtws
->base
.resource_create
= virgl_vtest_winsys_resource_cache_create
;
676 vtws
->base
.resource_reference
= virgl_vtest_resource_reference
;
677 vtws
->base
.resource_map
= virgl_vtest_resource_map
;
678 vtws
->base
.resource_wait
= virgl_vtest_resource_wait
;
679 vtws
->base
.resource_is_busy
= virgl_vtest_resource_is_busy
;
680 vtws
->base
.cmd_buf_create
= virgl_vtest_cmd_buf_create
;
681 vtws
->base
.cmd_buf_destroy
= virgl_vtest_cmd_buf_destroy
;
682 vtws
->base
.submit_cmd
= virgl_vtest_winsys_submit_cmd
;
684 vtws
->base
.emit_res
= virgl_vtest_emit_res
;
685 vtws
->base
.res_is_referenced
= virgl_vtest_res_is_ref
;
686 vtws
->base
.get_caps
= virgl_vtest_get_caps
;
688 vtws
->base
.cs_create_fence
= virgl_cs_create_fence
;
689 vtws
->base
.fence_wait
= virgl_fence_wait
;
690 vtws
->base
.fence_reference
= virgl_fence_reference
;
691 vtws
->base
.supports_fences
= 0;
692 vtws
->base
.supports_encoded_transfers
= (vtws
->protocol_version
>= 2);
694 vtws
->base
.flush_frontbuffer
= virgl_vtest_flush_frontbuffer
;