1/*
2 * Copyright 2014, 2015 Red Hat.
3 *
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:
10 *
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
13 * Software.
14 *
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.
22 */
23#include <stdio.h>
24#include "util/u_surface.h"
25#include "util/u_memory.h"
26#include "util/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"
31
32#include "virgl_vtest_winsys.h"
33#include "virgl_vtest_public.h"
34
35static void *virgl_vtest_resource_map(struct virgl_winsys *vws,
36                                      struct virgl_hw_res *res);
37static void virgl_vtest_resource_unmap(struct virgl_winsys *vws,
38                                       struct virgl_hw_res *res);
39
40static inline boolean can_cache_resource(struct virgl_hw_res *res)
41{
42   return res->cacheable == TRUE;
43}
44
45static uint32_t vtest_get_transfer_size(struct virgl_hw_res *res,
46                                        const struct pipe_box *box,
47                                        uint32_t stride, uint32_t layer_stride,
48                                        uint32_t level, uint32_t *valid_stride_p)
49{
50   uint32_t valid_stride, valid_layer_stride;
51
52   valid_stride = util_format_get_stride(res->format, box->width);
53   if (stride) {
54      if (box->height > 1)
55         valid_stride = stride;
56   }
57
58   valid_layer_stride = util_format_get_2d_size(res->format, valid_stride,
59                                                box->height);
60   if (layer_stride) {
61      if (box->depth > 1)
62         valid_layer_stride = layer_stride;
63   }
64
65   *valid_stride_p = valid_stride;
66   return valid_layer_stride * box->depth;
67}
68
69static int
70virgl_vtest_transfer_put(struct virgl_winsys *vws,
71                         struct virgl_hw_res *res,
72                         const struct pipe_box *box,
73                         uint32_t stride, uint32_t layer_stride,
74                         uint32_t buf_offset, uint32_t level)
75{
76   struct virgl_vtest_winsys *vtws = virgl_vtest_winsys(vws);
77   uint32_t size;
78   void *ptr;
79   uint32_t valid_stride;
80
81   size = vtest_get_transfer_size(res, box, stride, layer_stride, level,
82                                  &valid_stride);
83
84   virgl_vtest_send_transfer_put(vtws, res->res_handle,
85                                 level, stride, layer_stride,
86                                 box, size, buf_offset);
87
88   if (vtws->protocol_version >= 2)
89      return 0;
90
91   ptr = virgl_vtest_resource_map(vws, res);
92   virgl_vtest_send_transfer_put_data(vtws, ptr + buf_offset, size);
93   virgl_vtest_resource_unmap(vws, res);
94   return 0;
95}
96
97static int
98virgl_vtest_transfer_get_internal(struct virgl_winsys *vws,
99                                  struct virgl_hw_res *res,
100                                  const struct pipe_box *box,
101                                  uint32_t stride, uint32_t layer_stride,
102                                  uint32_t buf_offset, uint32_t level,
103                                  bool flush_front_buffer)
104{
105   struct virgl_vtest_winsys *vtws = virgl_vtest_winsys(vws);
106   uint32_t size;
107   void *ptr;
108   uint32_t valid_stride;
109
110   size = vtest_get_transfer_size(res, box, stride, layer_stride, level,
111                                  &valid_stride);
112   virgl_vtest_send_transfer_get(vtws, res->res_handle,
113                                 level, stride, layer_stride,
114                                 box, size, buf_offset);
115
116   if (flush_front_buffer || vtws->protocol_version >= 2)
117      virgl_vtest_busy_wait(vtws, res->res_handle, VCMD_BUSY_WAIT_FLAG_WAIT);
118
119   if (vtws->protocol_version >= 2) {
120      if (flush_front_buffer) {
121         if (box->depth > 1 || box->z > 1) {
122            fprintf(stderr, "Expected a 2D resource, received a 3D resource\n");
123            return -1;
124         }
125
126         void *dt_map;
127         uint32_t shm_stride;
128
129         /*
130          * The display target is aligned to 64 bytes, while the shared resource
131          * between the client/server is not.
132          */
133         shm_stride = util_format_get_stride(res->format, res->width);
134         ptr = virgl_vtest_resource_map(vws, res);
135         dt_map = vtws->sws->displaytarget_map(vtws->sws, res->dt, 0);
136
137         util_copy_rect(dt_map, res->format, res->stride, box->x, box->y,
138                        box->width, box->height, ptr, shm_stride, box->x,
139                        box->y);
140
141         virgl_vtest_resource_unmap(vws, res);
142         vtws->sws->displaytarget_unmap(vtws->sws, res->dt);
143      }
144   } else {
145      ptr = virgl_vtest_resource_map(vws, res);
146      virgl_vtest_recv_transfer_get_data(vtws, ptr + buf_offset, size,
147                                         valid_stride, box, res->format);
148      virgl_vtest_resource_unmap(vws, res);
149   }
150
151   return 0;
152}
153
154static int
155virgl_vtest_transfer_get(struct virgl_winsys *vws,
156                         struct virgl_hw_res *res,
157                         const struct pipe_box *box,
158                         uint32_t stride, uint32_t layer_stride,
159                         uint32_t buf_offset, uint32_t level)
160{
161   return virgl_vtest_transfer_get_internal(vws, res, box, stride,
162                                            layer_stride, buf_offset,
163                                            level, false);
164}
165
166static void virgl_hw_res_destroy(struct virgl_vtest_winsys *vtws,
167                                 struct virgl_hw_res *res)
168{
169   virgl_vtest_send_resource_unref(vtws, res->res_handle);
170   if (res->dt)
171      vtws->sws->displaytarget_destroy(vtws->sws, res->dt);
172   if (vtws->protocol_version >= 2) {
173      if (res->ptr)
174         os_munmap(res->ptr, res->size);
175   } else {
176      free(res->ptr);
177   }
178
179   FREE(res);
180}
181
182static boolean virgl_vtest_resource_is_busy(struct virgl_vtest_winsys *vtws,
183                                            struct virgl_hw_res *res)
184{
185   /* implement busy check */
186   int ret;
187   ret = virgl_vtest_busy_wait(vtws, res->res_handle, 0);
188
189   if (ret < 0)
190      return FALSE;
191
192   return ret == 1 ? TRUE : FALSE;
193}
194
195static void
196virgl_cache_flush(struct virgl_vtest_winsys *vtws)
197{
198   struct list_head *curr, *next;
199   struct virgl_hw_res *res;
200
201   mtx_lock(&vtws->mutex);
202   curr = vtws->delayed.next;
203   next = curr->next;
204
205   while (curr != &vtws->delayed) {
206      res = LIST_ENTRY(struct virgl_hw_res, curr, head);
207      LIST_DEL(&res->head);
208      virgl_hw_res_destroy(vtws, res);
209      curr = next;
210      next = curr->next;
211   }
212   mtx_unlock(&vtws->mutex);
213}
214
215static void
216virgl_cache_list_check_free(struct virgl_vtest_winsys *vtws)
217{
218   struct list_head *curr, *next;
219   struct virgl_hw_res *res;
220   int64_t now;
221
222   now = os_time_get();
223   curr = vtws->delayed.next;
224   next = curr->next;
225   while (curr != &vtws->delayed) {
226      res = LIST_ENTRY(struct virgl_hw_res, curr, head);
227      if (!os_time_timeout(res->start, res->end, now))
228         break;
229
230      LIST_DEL(&res->head);
231      virgl_hw_res_destroy(vtws, res);
232      curr = next;
233      next = curr->next;
234   }
235}
236
237static void virgl_vtest_resource_reference(struct virgl_vtest_winsys *vtws,
238                                           struct virgl_hw_res **dres,
239                                           struct virgl_hw_res *sres)
240{
241   struct virgl_hw_res *old = *dres;
242   if (pipe_reference(&(*dres)->reference, &sres->reference)) {
243      if (!can_cache_resource(old)) {
244         virgl_hw_res_destroy(vtws, old);
245      } else {
246         mtx_lock(&vtws->mutex);
247         virgl_cache_list_check_free(vtws);
248
249         old->start = os_time_get();
250         old->end = old->start + vtws->usecs;
251         LIST_ADDTAIL(&old->head, &vtws->delayed);
252         vtws->num_delayed++;
253         mtx_unlock(&vtws->mutex);
254      }
255   }
256   *dres = sres;
257}
258
259static struct virgl_hw_res *
260virgl_vtest_winsys_resource_create(struct virgl_winsys *vws,
261                                   enum pipe_texture_target target,
262                                   uint32_t format,
263                                   uint32_t bind,
264                                   uint32_t width,
265                                   uint32_t height,
266                                   uint32_t depth,
267                                   uint32_t array_size,
268                                   uint32_t last_level,
269                                   uint32_t nr_samples,
270                                   uint32_t size)
271{
272   struct virgl_vtest_winsys *vtws = virgl_vtest_winsys(vws);
273   struct virgl_hw_res *res;
274   static int handle = 1;
275   int fd = -1;
276
277   res = CALLOC_STRUCT(virgl_hw_res);
278   if (!res)
279      return NULL;
280
281   if (bind & (VIRGL_BIND_DISPLAY_TARGET | VIRGL_BIND_SCANOUT)) {
282      res->dt = vtws->sws->displaytarget_create(vtws->sws, bind, format,
283                                                width, height, 64, NULL,
284                                                &res->stride);
285
286   } else if (vtws->protocol_version < 2) {
287      res->ptr = align_malloc(size, 64);
288      if (!res->ptr) {
289         FREE(res);
290         return NULL;
291      }
292   }
293
294   res->bind = bind;
295   res->format = format;
296   res->height = height;
297   res->width = width;
298   res->size = size;
299   virgl_vtest_send_resource_create(vtws, handle, target, format, bind,
300                                    width, height, depth, array_size,
301                                    last_level, nr_samples, size, &fd);
302
303   if (vtws->protocol_version >= 2) {
304      if (res->size == 0) {
305         res->ptr = NULL;
306         goto out;
307      }
308
309      if (fd < 0) {
310         FREE(res);
311         fprintf(stderr, "Unable to get a valid fd\n");
312         return NULL;
313      }
314
315      res->ptr = os_mmap(NULL, res->size, PROT_WRITE | PROT_READ, MAP_SHARED,
316                         fd, 0);
317
318      if (res->ptr == MAP_FAILED) {
319         fprintf(stderr, "Client failed to map shared memory region\n");
320         close(fd);
321         FREE(res);
322         return NULL;
323      }
324
325      close(fd);
326   }
327
328out:
329   res->res_handle = handle++;
330   pipe_reference_init(&res->reference, 1);
331   p_atomic_set(&res->num_cs_references, 0);
332   return res;
333}
334
335static void virgl_vtest_winsys_resource_unref(struct virgl_winsys *vws,
336                                              struct virgl_hw_res *hres)
337{
338   struct virgl_vtest_winsys *vtws = virgl_vtest_winsys(vws);
339   virgl_vtest_resource_reference(vtws, &hres, NULL);
340}
341
342static void *virgl_vtest_resource_map(struct virgl_winsys *vws,
343                                      struct virgl_hw_res *res)
344{
345   struct virgl_vtest_winsys *vtws = virgl_vtest_winsys(vws);
346
347   /*
348    * With protocol v0 we can either have a display target or a resource backing
349    * store. With protocol v2 we can have both, so only return the memory mapped
350    * backing store in this function. We can copy to the display target when
351    * appropriate.
352    */
353   if (vtws->protocol_version >= 2 || !res->dt) {
354      res->mapped = res->ptr;
355      return res->mapped;
356   } else {
357      return vtws->sws->displaytarget_map(vtws->sws, res->dt, 0);
358   }
359}
360
361static void virgl_vtest_resource_unmap(struct virgl_winsys *vws,
362                                       struct virgl_hw_res *res)
363{
364   struct virgl_vtest_winsys *vtws = virgl_vtest_winsys(vws);
365   if (res->mapped)
366      res->mapped = NULL;
367
368   if (res->dt && vtws->protocol_version < 2)
369      vtws->sws->displaytarget_unmap(vtws->sws, res->dt);
370}
371
372static void virgl_vtest_resource_wait(struct virgl_winsys *vws,
373                                      struct virgl_hw_res *res)
374{
375   struct virgl_vtest_winsys *vtws = virgl_vtest_winsys(vws);
376
377   virgl_vtest_busy_wait(vtws, res->res_handle, VCMD_BUSY_WAIT_FLAG_WAIT);
378}
379
380static inline int virgl_is_res_compat(struct virgl_vtest_winsys *vtws,
381                                      struct virgl_hw_res *res,
382                                      uint32_t size, uint32_t bind,
383                                      uint32_t format)
384{
385   if (res->bind != bind)
386      return 0;
387   if (res->format != format)
388      return 0;
389   if (res->size < size)
390      return 0;
391   if (res->size > size * 2)
392      return 0;
393
394   if (virgl_vtest_resource_is_busy(vtws, res)) {
395      return -1;
396   }
397
398   return 1;
399}
400
401static struct virgl_hw_res *
402virgl_vtest_winsys_resource_cache_create(struct virgl_winsys *vws,
403                                         enum pipe_texture_target target,
404                                         uint32_t format,
405                                         uint32_t bind,
406                                         uint32_t width,
407                                         uint32_t height,
408                                         uint32_t depth,
409                                         uint32_t array_size,
410                                         uint32_t last_level,
411                                         uint32_t nr_samples,
412                                         uint32_t size)
413{
414   struct virgl_vtest_winsys *vtws = virgl_vtest_winsys(vws);
415   struct virgl_hw_res *res, *curr_res;
416   struct list_head *curr, *next;
417   int64_t now;
418   int ret = -1;
419
420   /* only store binds for vertex/index/const buffers */
421   if (bind != VIRGL_BIND_CONSTANT_BUFFER && bind != VIRGL_BIND_INDEX_BUFFER &&
422       bind != VIRGL_BIND_VERTEX_BUFFER && bind != VIRGL_BIND_CUSTOM)
423      goto alloc;
424
425   mtx_lock(&vtws->mutex);
426
427   res = NULL;
428   curr = vtws->delayed.next;
429   next = curr->next;
430
431   now = os_time_get();
432   while (curr != &vtws->delayed) {
433      curr_res = LIST_ENTRY(struct virgl_hw_res, curr, head);
434
435      if (!res && ((ret = virgl_is_res_compat(vtws, curr_res, size, bind, format)) > 0))
436         res = curr_res;
437      else if (os_time_timeout(curr_res->start, curr_res->end, now)) {
438         LIST_DEL(&curr_res->head);
439         virgl_hw_res_destroy(vtws, curr_res);
440      } else
441         break;
442
443      if (ret == -1)
444         break;
445
446      curr = next;
447      next = curr->next;
448   }
449
450   if (!res && ret != -1) {
451      while (curr != &vtws->delayed) {
452         curr_res = LIST_ENTRY(struct virgl_hw_res, curr, head);
453         ret = virgl_is_res_compat(vtws, curr_res, size, bind, format);
454         if (ret > 0) {
455            res = curr_res;
456            break;
457         }
458         if (ret == -1)
459            break;
460         curr = next;
461         next = curr->next;
462      }
463   }
464
465   if (res) {
466      LIST_DEL(&res->head);
467      --vtws->num_delayed;
468      mtx_unlock(&vtws->mutex);
469      pipe_reference_init(&res->reference, 1);
470      return res;
471   }
472
473   mtx_unlock(&vtws->mutex);
474
475alloc:
476   res = virgl_vtest_winsys_resource_create(vws, target, format, bind,
477                                            width, height, depth, array_size,
478                                            last_level, nr_samples, size);
479   if (bind == VIRGL_BIND_CONSTANT_BUFFER || bind == VIRGL_BIND_INDEX_BUFFER ||
480       bind == VIRGL_BIND_VERTEX_BUFFER)
481      res->cacheable = TRUE;
482   return res;
483}
484
485static boolean virgl_vtest_lookup_res(struct virgl_vtest_cmd_buf *cbuf,
486                                      struct virgl_hw_res *res)
487{
488   unsigned hash = res->res_handle & (sizeof(cbuf->is_handle_added)-1);
489   int i;
490
491   if (cbuf->is_handle_added[hash]) {
492      i = cbuf->reloc_indices_hashlist[hash];
493      if (cbuf->res_bo[i] == res)
494         return true;
495
496      for (i = 0; i < cbuf->cres; i++) {
497         if (cbuf->res_bo[i] == res) {
498            cbuf->reloc_indices_hashlist[hash] = i;
499            return true;
500         }
501      }
502   }
503   return false;
504}
505
506static void virgl_vtest_release_all_res(struct virgl_vtest_winsys *vtws,
507                                        struct virgl_vtest_cmd_buf *cbuf)
508{
509   int i;
510
511   for (i = 0; i < cbuf->cres; i++) {
512      p_atomic_dec(&cbuf->res_bo[i]->num_cs_references);
513      virgl_vtest_resource_reference(vtws, &cbuf->res_bo[i], NULL);
514   }
515   cbuf->cres = 0;
516}
517
518static void virgl_vtest_add_res(struct virgl_vtest_winsys *vtws,
519                                struct virgl_vtest_cmd_buf *cbuf,
520                                struct virgl_hw_res *res)
521{
522   unsigned hash = res->res_handle & (sizeof(cbuf->is_handle_added)-1);
523
524   if (cbuf->cres >= cbuf->nres) {
525      unsigned new_nres = cbuf->nres + 256;
526      struct virgl_hw_res **new_re_bo = REALLOC(cbuf->res_bo,
527                                                cbuf->nres * sizeof(struct virgl_hw_buf*),
528                                                new_nres * sizeof(struct virgl_hw_buf*));
529      if (!new_re_bo) {
530          fprintf(stderr,"failure to add relocation %d, %d\n", cbuf->cres, cbuf->nres);
531          return;
532      }
533
534      cbuf->res_bo = new_re_bo;
535      cbuf->nres = new_nres;
536   }
537
538   cbuf->res_bo[cbuf->cres] = NULL;
539   virgl_vtest_resource_reference(vtws, &cbuf->res_bo[cbuf->cres], res);
540   cbuf->is_handle_added[hash] = TRUE;
541
542   cbuf->reloc_indices_hashlist[hash] = cbuf->cres;
543   p_atomic_inc(&res->num_cs_references);
544   cbuf->cres++;
545}
546
547static struct virgl_cmd_buf *virgl_vtest_cmd_buf_create(struct virgl_winsys *vws,
548                                                        uint32_t size)
549{
550   struct virgl_vtest_cmd_buf *cbuf;
551
552   cbuf = CALLOC_STRUCT(virgl_vtest_cmd_buf);
553   if (!cbuf)
554      return NULL;
555
556   cbuf->nres = 512;
557   cbuf->res_bo = CALLOC(cbuf->nres, sizeof(struct virgl_hw_buf*));
558   if (!cbuf->res_bo) {
559      FREE(cbuf);
560      return NULL;
561   }
562
563   cbuf->buf = CALLOC(size, sizeof(uint32_t));
564   if (!cbuf->buf) {
565      FREE(cbuf->res_bo);
566      FREE(cbuf);
567      return NULL;
568   }
569
570   cbuf->ws = vws;
571   cbuf->base.buf = cbuf->buf;
572   return &cbuf->base;
573}
574
575static void virgl_vtest_cmd_buf_destroy(struct virgl_cmd_buf *_cbuf)
576{
577   struct virgl_vtest_cmd_buf *cbuf = virgl_vtest_cmd_buf(_cbuf);
578
579   virgl_vtest_release_all_res(virgl_vtest_winsys(cbuf->ws), cbuf);
580   FREE(cbuf->res_bo);
581   FREE(cbuf->buf);
582   FREE(cbuf);
583}
584
585static struct pipe_fence_handle *
586virgl_vtest_fence_create(struct virgl_winsys *vws)
587{
588   struct virgl_hw_res *res;
589
590   res = virgl_vtest_winsys_resource_cache_create(vws,
591                                                PIPE_BUFFER,
592                                                PIPE_FORMAT_R8_UNORM,
593                                                VIRGL_BIND_CUSTOM,
594                                                8, 1, 1, 0, 0, 0, 8);
595
596   return (struct pipe_fence_handle *)res;
597}
598
599static int virgl_vtest_winsys_submit_cmd(struct virgl_winsys *vws,
600                                         struct virgl_cmd_buf *_cbuf,
601                                         struct pipe_fence_handle **fence)
602{
603   struct virgl_vtest_winsys *vtws = virgl_vtest_winsys(vws);
604   struct virgl_vtest_cmd_buf *cbuf = virgl_vtest_cmd_buf(_cbuf);
605   int ret;
606
607   if (cbuf->base.cdw == 0)
608      return 0;
609
610   ret = virgl_vtest_submit_cmd(vtws, cbuf);
611   if (fence && ret == 0)
612      *fence = virgl_vtest_fence_create(vws);
613
614   virgl_vtest_release_all_res(vtws, cbuf);
615   memset(cbuf->is_handle_added, 0, sizeof(cbuf->is_handle_added));
616   cbuf->base.cdw = 0;
617   return ret;
618}
619
620static void virgl_vtest_emit_res(struct virgl_winsys *vws,
621                                 struct virgl_cmd_buf *_cbuf,
622                                 struct virgl_hw_res *res, boolean write_buf)
623{
624   struct virgl_vtest_winsys *vtws = virgl_vtest_winsys(vws);
625   struct virgl_vtest_cmd_buf *cbuf = virgl_vtest_cmd_buf(_cbuf);
626   boolean already_in_list = virgl_vtest_lookup_res(cbuf, res);
627
628   if (write_buf)
629      cbuf->base.buf[cbuf->base.cdw++] = res->res_handle;
630   if (!already_in_list)
631      virgl_vtest_add_res(vtws, cbuf, res);
632}
633
634static boolean virgl_vtest_res_is_ref(struct virgl_winsys *vws,
635                                      struct virgl_cmd_buf *_cbuf,
636                                      struct virgl_hw_res *res)
637{
638   if (!p_atomic_read(&res->num_cs_references))
639      return FALSE;
640
641   return TRUE;
642}
643
644static int virgl_vtest_get_caps(struct virgl_winsys *vws,
645                                struct virgl_drm_caps *caps)
646{
647   struct virgl_vtest_winsys *vtws = virgl_vtest_winsys(vws);
648
649   virgl_ws_fill_new_caps_defaults(caps);
650   return virgl_vtest_send_get_caps(vtws, caps);
651}
652
653static struct pipe_fence_handle *
654virgl_cs_create_fence(struct virgl_winsys *vws, int fd)
655{
656   return virgl_vtest_fence_create(vws);
657}
658
659static bool virgl_fence_wait(struct virgl_winsys *vws,
660                             struct pipe_fence_handle *fence,
661                             uint64_t timeout)
662{
663   struct virgl_vtest_winsys *vdws = virgl_vtest_winsys(vws);
664   struct virgl_hw_res *res = virgl_hw_res(fence);
665
666   if (timeout == 0)
667      return !virgl_vtest_resource_is_busy(vdws, res);
668
669   if (timeout != PIPE_TIMEOUT_INFINITE) {
670      int64_t start_time = os_time_get();
671      timeout /= 1000;
672      while (virgl_vtest_resource_is_busy(vdws, res)) {
673         if (os_time_get() - start_time >= timeout)
674            return FALSE;
675         os_time_sleep(10);
676      }
677      return TRUE;
678   }
679   virgl_vtest_resource_wait(vws, res);
680   return TRUE;
681}
682
683static void virgl_fence_reference(struct virgl_winsys *vws,
684                                  struct pipe_fence_handle **dst,
685                                  struct pipe_fence_handle *src)
686{
687   struct virgl_vtest_winsys *vdws = virgl_vtest_winsys(vws);
688   virgl_vtest_resource_reference(vdws, (struct virgl_hw_res **)dst,
689                                  virgl_hw_res(src));
690}
691
692static void virgl_vtest_flush_frontbuffer(struct virgl_winsys *vws,
693                                          struct virgl_hw_res *res,
694                                          unsigned level, unsigned layer,
695                                          void *winsys_drawable_handle,
696                                          struct pipe_box *sub_box)
697{
698   struct virgl_vtest_winsys *vtws = virgl_vtest_winsys(vws);
699   struct pipe_box box;
700   uint32_t offset = 0;
701   if (!res->dt)
702      return;
703
704   memset(&box, 0, sizeof(box));
705
706   if (sub_box) {
707      box = *sub_box;
708      offset = box.y / util_format_get_blockheight(res->format) * res->stride +
709               box.x / util_format_get_blockwidth(res->format) * util_format_get_blocksize(res->format);
710   } else {
711      box.z = layer;
712      box.width = res->width;
713      box.height = res->height;
714      box.depth = 1;
715   }
716
717   virgl_vtest_transfer_get_internal(vws, res, &box, res->stride, 0, offset,
718                                     level, true);
719
720   vtws->sws->displaytarget_display(vtws->sws, res->dt, winsys_drawable_handle,
721                                    sub_box);
722}
723
724static void
725virgl_vtest_winsys_destroy(struct virgl_winsys *vws)
726{
727   struct virgl_vtest_winsys *vtws = virgl_vtest_winsys(vws);
728
729   virgl_cache_flush(vtws);
730
731   mtx_destroy(&vtws->mutex);
732   FREE(vtws);
733}
734
735struct virgl_winsys *
736virgl_vtest_winsys_wrap(struct sw_winsys *sws)
737{
738   struct virgl_vtest_winsys *vtws;
739
740   vtws = CALLOC_STRUCT(virgl_vtest_winsys);
741   if (!vtws)
742      return NULL;
743
744   virgl_vtest_connect(vtws);
745   vtws->sws = sws;
746
747   vtws->usecs = 1000000;
748   LIST_INITHEAD(&vtws->delayed);
749   (void) mtx_init(&vtws->mutex, mtx_plain);
750
751   vtws->base.destroy = virgl_vtest_winsys_destroy;
752
753   vtws->base.transfer_put = virgl_vtest_transfer_put;
754   vtws->base.transfer_get = virgl_vtest_transfer_get;
755
756   vtws->base.resource_create = virgl_vtest_winsys_resource_cache_create;
757   vtws->base.resource_unref = virgl_vtest_winsys_resource_unref;
758   vtws->base.resource_map = virgl_vtest_resource_map;
759   vtws->base.resource_wait = virgl_vtest_resource_wait;
760   vtws->base.cmd_buf_create = virgl_vtest_cmd_buf_create;
761   vtws->base.cmd_buf_destroy = virgl_vtest_cmd_buf_destroy;
762   vtws->base.submit_cmd = virgl_vtest_winsys_submit_cmd;
763
764   vtws->base.emit_res = virgl_vtest_emit_res;
765   vtws->base.res_is_referenced = virgl_vtest_res_is_ref;
766   vtws->base.get_caps = virgl_vtest_get_caps;
767
768   vtws->base.cs_create_fence = virgl_cs_create_fence;
769   vtws->base.fence_wait = virgl_fence_wait;
770   vtws->base.fence_reference = virgl_fence_reference;
771   vtws->base.supports_fences =  0;
772   vtws->base.supports_encoded_transfers = (vtws->protocol_version >= 2);
773
774   vtws->base.flush_frontbuffer = virgl_vtest_flush_frontbuffer;
775
776   return &vtws->base;
777}
778