virgl/vtest: add vtest driver

virgl/vtest is a swrast driver that allows the
virgl acceleration to be tested without having
a virtual machine.

The backend has a unix socket server that
this connects to.

This is run by setting
LIBGL_ALWAYS_SOFTWARE=y
GALLIUM_DRIVER=virpipe

In this mode all renderering is sent over
a socket to the remote renderer, and the
results are readback and copies to the screen
using drisw. This works well enough to develop
new features and to help debug.

Signed-off-by: Dave Airlie <airlied@redhat.com>
This commit is contained in:
Dave Airlie 2015-03-13 14:15:47 +10:00 committed by Dave Airlie
parent a8987b88ff
commit b3b82fe8ea
11 changed files with 1243 additions and 2 deletions

View file

@ -2425,6 +2425,7 @@ AC_CONFIG_FILES([Makefile
src/gallium/winsys/sw/xlib/Makefile
src/gallium/winsys/vc4/drm/Makefile
src/gallium/winsys/virgl/drm/Makefile
src/gallium/winsys/virgl/vtest/Makefile
src/gbm/Makefile
src/gbm/main/gbm.pc
src/glsl/Makefile

View file

@ -84,7 +84,7 @@ endif
## virgl
if HAVE_GALLIUM_VIRGL
SUBDIRS += drivers/virgl winsys/virgl/drm
SUBDIRS += drivers/virgl winsys/virgl/drm winsys/virgl/vtest
endif
## the sw winsys'

View file

@ -19,6 +19,10 @@
#include "llvmpipe/lp_public.h"
#endif
#ifdef GALLIUM_VIRGL
#include "virgl/virgl_public.h"
#include "virgl/vtest/virgl_vtest_public.h"
#endif
static inline struct pipe_screen *
sw_screen_create_named(struct sw_winsys *winsys, const char *driver)
@ -30,6 +34,14 @@ sw_screen_create_named(struct sw_winsys *winsys, const char *driver)
screen = llvmpipe_create_screen(winsys);
#endif
#if defined(GALLIUM_VIRGL)
if (screen == NULL && strcmp(driver, "virpipe") == 0) {
struct virgl_winsys *vws;
vws = virgl_vtest_winsys_wrap(winsys);
screen = virgl_create_screen(vws);
}
#endif
#if defined(GALLIUM_SOFTPIPE)
if (screen == NULL)
screen = softpipe_create_screen(winsys);

View file

@ -4,7 +4,8 @@ TARGET_DRIVERS += virtio_gpu
TARGET_CPPFLAGS += -DGALLIUM_VIRGL
TARGET_LIB_DEPS += \
$(top_builddir)/src/gallium/drivers/virgl/libvirgl.la \
$(top_builddir)/src/gallium/winsys/virgl/drm/libvirgldrm.la
$(top_builddir)/src/gallium/winsys/virgl/drm/libvirgldrm.la \
$(top_builddir)/src/gallium/winsys/virgl/vtest/libvirglvtest.la \
$(LIBDRM_LIBS)
endif

View file

@ -0,0 +1,33 @@
# Copyright © 2015 Red Hat
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the "Software"),
# to deal in the Software without restriction, including without limitation
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice (including the next
# paragraph) shall be included in all copies or substantial portions of the
# Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE.
include Makefile.sources
include $(top_srcdir)/src/gallium/Automake.inc
AM_CFLAGS = \
-I$(top_srcdir)/include \
-I$(top_srcdir)/src/gallium/drivers \
$(GALLIUM_CFLAGS)
noinst_LTLIBRARIES = libvirglvtest.la
libvirglvtest_la_SOURCES = $(C_SOURCES)

View file

@ -0,0 +1,3 @@
C_SOURCES := \
virgl_vtest_winsys.c \
virgl_vtest_socket.c

View file

@ -0,0 +1,30 @@
/*
* Copyright 2014, 2015 Red Hat.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* on the rights to use, copy, modify, merge, publish, distribute, sub
* license, and/or sell copies of the Software, and to permit persons to whom
* the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
* THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
* USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef VIRGL_VTEST_PUBLIC_H
#define VIRGL_VTEST_PUBLIC_H
struct virgl_winsys;
struct virgl_winsys *virgl_vtest_winsys_wrap(struct sw_winsys *sws);
#endif

View file

@ -0,0 +1,288 @@
/*
* Copyright 2014, 2015 Red Hat.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* on the rights to use, copy, modify, merge, publish, distribute, sub
* license, and/or sell copies of the Software, and to permit persons to whom
* the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
* THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
* USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#define _FILE_OFFSET_BITS 64
#include "virgl_vtest_winsys.h"
#include "virgl_vtest_public.h"
#include <sys/socket.h>
#include <errno.h>
#include <stdio.h>
#include <netinet/in.h>
#include <sys/un.h>
#include <unistd.h>
#include <os/os_process.h>
#include <util/u_format.h>
/* connect to remote socket */
#define VTEST_SOCKET_NAME "/tmp/.virgl_test"
/* block read/write routines */
static int virgl_block_write(int fd, void *buf, int size)
{
void *ptr = buf;
int left;
int ret;
left = size;
do {
ret = write(fd, ptr, left);
if (ret < 0)
return -errno;
left -= ret;
ptr += ret;
} while (left);
return size;
}
static int virgl_block_read(int fd, void *buf, int size)
{
void *ptr = buf;
int left;
int ret;
left = size;
do {
ret = read(fd, ptr, left);
if (ret <= 0) {
fprintf(stderr, "lost connection to rendering server on %d read %d %d\n", size, ret, errno);
abort();
return ret < 0 ? -errno : 0;
}
left -= ret;
ptr += ret;
} while (left);
return size;
}
static int virgl_vtest_send_init(struct virgl_vtest_winsys *vws)
{
uint32_t buf[VTEST_HDR_SIZE];
const char *nstr = "virtest";
char cmdline[64];
int ret;
ret = os_get_process_name(cmdline, 63);
if (ret == FALSE)
strcpy(cmdline, nstr);
#if defined(__GLIBC__) || defined(__CYGWIN__)
if (!strcmp(cmdline, "shader_runner")) {
const char *name;
/* hack to get better testname */
name = program_invocation_short_name;
name += strlen(name) + 1;
strncpy(cmdline, name, 63);
}
#endif
buf[VTEST_CMD_LEN] = strlen(cmdline) + 1;
buf[VTEST_CMD_ID] = VCMD_CREATE_RENDERER;
virgl_block_write(vws->sock_fd, &buf, sizeof(buf));
virgl_block_write(vws->sock_fd, (void *)cmdline, strlen(cmdline) + 1);
return 0;
}
int virgl_vtest_connect(struct virgl_vtest_winsys *vws)
{
struct sockaddr_un un;
int sock, ret;
sock = socket(PF_UNIX, SOCK_STREAM, 0);
if (sock < 0)
return -1;
memset(&un, 0, sizeof(un));
un.sun_family = AF_UNIX;
snprintf(un.sun_path, sizeof(un.sun_path), "%s", VTEST_SOCKET_NAME);
do {
ret = 0;
if (connect(sock, (struct sockaddr *)&un, sizeof(un)) < 0) {
ret = -errno;
}
} while (ret == -EINTR);
vws->sock_fd = sock;
virgl_vtest_send_init(vws);
return 0;
}
int virgl_vtest_send_get_caps(struct virgl_vtest_winsys *vws,
struct virgl_drm_caps *caps)
{
uint32_t get_caps_buf[VTEST_HDR_SIZE];
uint32_t resp_buf[VTEST_HDR_SIZE];
int ret;
get_caps_buf[VTEST_CMD_LEN] = 0;
get_caps_buf[VTEST_CMD_ID] = VCMD_GET_CAPS;
virgl_block_write(vws->sock_fd, &get_caps_buf, sizeof(get_caps_buf));
ret = virgl_block_read(vws->sock_fd, resp_buf, sizeof(resp_buf));
if (ret <= 0)
return 0;
ret = virgl_block_read(vws->sock_fd, &caps->caps, sizeof(union virgl_caps));
return 0;
}
int virgl_vtest_send_resource_create(struct virgl_vtest_winsys *vws,
uint32_t handle,
enum pipe_texture_target target,
uint32_t format,
uint32_t bind,
uint32_t width,
uint32_t height,
uint32_t depth,
uint32_t array_size,
uint32_t last_level,
uint32_t nr_samples)
{
uint32_t res_create_buf[VCMD_RES_CREATE_SIZE], vtest_hdr[VTEST_HDR_SIZE];
vtest_hdr[VTEST_CMD_LEN] = VCMD_RES_CREATE_SIZE;
vtest_hdr[VTEST_CMD_ID] = VCMD_RESOURCE_CREATE;
res_create_buf[VCMD_RES_CREATE_RES_HANDLE] = handle;
res_create_buf[VCMD_RES_CREATE_TARGET] = target;
res_create_buf[VCMD_RES_CREATE_FORMAT] = format;
res_create_buf[VCMD_RES_CREATE_BIND] = bind;
res_create_buf[VCMD_RES_CREATE_WIDTH] = width;
res_create_buf[VCMD_RES_CREATE_HEIGHT] = height;
res_create_buf[VCMD_RES_CREATE_DEPTH] = depth;
res_create_buf[VCMD_RES_CREATE_ARRAY_SIZE] = array_size;
res_create_buf[VCMD_RES_CREATE_LAST_LEVEL] = last_level;
res_create_buf[VCMD_RES_CREATE_NR_SAMPLES] = nr_samples;
virgl_block_write(vws->sock_fd, &vtest_hdr, sizeof(vtest_hdr));
virgl_block_write(vws->sock_fd, &res_create_buf, sizeof(res_create_buf));
return 0;
}
int virgl_vtest_submit_cmd(struct virgl_vtest_winsys *vws,
struct virgl_vtest_cmd_buf *cbuf)
{
uint32_t vtest_hdr[VTEST_HDR_SIZE];
vtest_hdr[VTEST_CMD_LEN] = cbuf->base.cdw;
vtest_hdr[VTEST_CMD_ID] = VCMD_SUBMIT_CMD;
virgl_block_write(vws->sock_fd, &vtest_hdr, sizeof(vtest_hdr));
virgl_block_write(vws->sock_fd, cbuf->buf, cbuf->base.cdw * 4);
return 0;
}
int virgl_vtest_send_resource_unref(struct virgl_vtest_winsys *vws,
uint32_t handle)
{
uint32_t vtest_hdr[VTEST_HDR_SIZE];
uint32_t cmd[1];
vtest_hdr[VTEST_CMD_LEN] = 1;
vtest_hdr[VTEST_CMD_ID] = VCMD_RESOURCE_UNREF;
cmd[0] = handle;
virgl_block_write(vws->sock_fd, &vtest_hdr, sizeof(vtest_hdr));
virgl_block_write(vws->sock_fd, &cmd, sizeof(cmd));
return 0;
}
int virgl_vtest_send_transfer_cmd(struct virgl_vtest_winsys *vws,
uint32_t vcmd,
uint32_t handle,
uint32_t level, uint32_t stride,
uint32_t layer_stride,
const struct pipe_box *box,
uint32_t data_size)
{
uint32_t vtest_hdr[VTEST_HDR_SIZE];
uint32_t cmd[VCMD_TRANSFER_HDR_SIZE];
bool is_put = (vcmd == VCMD_TRANSFER_PUT);
vtest_hdr[VTEST_CMD_LEN] = VCMD_TRANSFER_HDR_SIZE + (is_put ? (data_size + 3 / 4) : 0);
vtest_hdr[VTEST_CMD_ID] = vcmd;
cmd[0] = handle;
cmd[1] = level;
cmd[2] = stride;
cmd[3] = layer_stride;
cmd[4] = box->x;
cmd[5] = box->y;
cmd[6] = box->z;
cmd[7] = box->width;
cmd[8] = box->height;
cmd[9] = box->depth;
cmd[10] = data_size;
virgl_block_write(vws->sock_fd, &vtest_hdr, sizeof(vtest_hdr));
virgl_block_write(vws->sock_fd, &cmd, sizeof(cmd));
return 0;
}
int virgl_vtest_send_transfer_put_data(struct virgl_vtest_winsys *vws,
void *data,
uint32_t data_size)
{
return virgl_block_write(vws->sock_fd, data, data_size);
}
int virgl_vtest_recv_transfer_get_data(struct virgl_vtest_winsys *vws,
void *data,
uint32_t data_size,
uint32_t stride,
const struct pipe_box *box, uint32_t format)
{
void *line = malloc(stride);
void *ptr = data;
int hblocks = util_format_get_nblocksy(format, box->height);
line = malloc(stride);
while (hblocks) {
virgl_block_read(vws->sock_fd, line, stride);
memcpy(ptr, line, util_format_get_stride(format, box->width));
ptr += stride;
hblocks--;
}
free(line);
return 0;
}
int virgl_vtest_busy_wait(struct virgl_vtest_winsys *vws, int handle,
int flags)
{
uint32_t vtest_hdr[VTEST_HDR_SIZE];
uint32_t cmd[VCMD_BUSY_WAIT_SIZE];
uint32_t result[1];
int ret;
vtest_hdr[VTEST_CMD_LEN] = VCMD_BUSY_WAIT_SIZE;
vtest_hdr[VTEST_CMD_ID] = VCMD_RESOURCE_BUSY_WAIT;
cmd[VCMD_BUSY_WAIT_HANDLE] = handle;
cmd[VCMD_BUSY_WAIT_FLAGS] = flags;
virgl_block_write(vws->sock_fd, &vtest_hdr, sizeof(vtest_hdr));
virgl_block_write(vws->sock_fd, &cmd, sizeof(cmd));
ret = virgl_block_read(vws->sock_fd, vtest_hdr, sizeof(vtest_hdr));
assert(ret);
ret = virgl_block_read(vws->sock_fd, result, sizeof(result));
assert(ret);
return result[0];
}

View file

@ -0,0 +1,653 @@
/*
* Copyright 2014, 2015 Red Hat.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* on the rights to use, copy, modify, merge, publish, distribute, sub
* license, and/or sell copies of the Software, and to permit persons to whom
* the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
* THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
* USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#define _FILE_OFFSET_BITS 64
#include <stdio.h>
#include "virgl_vtest_winsys.h"
#include "virgl_vtest_public.h"
#include "util/u_memory.h"
#include "util/u_format.h"
#include "util/u_inlines.h"
#include "state_tracker/drm_driver.h"
#include "os/os_time.h"
static void *virgl_vtest_resource_map(struct virgl_winsys *vws, struct virgl_hw_res *res);
static void virgl_vtest_resource_unmap(struct virgl_winsys *vws, struct virgl_hw_res *res);
static inline boolean can_cache_resource(struct virgl_hw_res *res)
{
return res->cacheable == TRUE;
}
static uint32_t vtest_get_transfer_size(struct virgl_hw_res *res,
const struct pipe_box *box,
uint32_t stride, uint32_t layer_stride,
uint32_t level, uint32_t *valid_stride_p)
{
uint32_t valid_stride, valid_layer_stride;
valid_stride = util_format_get_stride(res->format, box->width);
if (stride) {
if (box->height > 1)
valid_stride = stride;
}
valid_layer_stride = util_format_get_2d_size(res->format, valid_stride,
box->height);
if (layer_stride) {
if (box->depth > 1)
valid_layer_stride = layer_stride;
}
*valid_stride_p = valid_stride;
return valid_layer_stride * box->depth;
}
static int
virgl_vtest_transfer_put(struct virgl_winsys *vws,
struct virgl_hw_res *res,
const struct pipe_box *box,
uint32_t stride, uint32_t layer_stride,
uint32_t buf_offset, uint32_t level)
{
struct virgl_vtest_winsys *vtws = virgl_vtest_winsys(vws);
uint32_t size;
void *ptr;
uint32_t valid_stride;
size = vtest_get_transfer_size(res, box, stride, layer_stride, level, &valid_stride);
virgl_vtest_send_transfer_cmd(vtws, VCMD_TRANSFER_PUT, res->res_handle,
level, stride, layer_stride,
box, size);
ptr = virgl_vtest_resource_map(vws, res);
virgl_vtest_send_transfer_put_data(vtws, ptr + buf_offset, size);
virgl_vtest_resource_unmap(vws, res);
return 0;
}
static int
virgl_vtest_transfer_get(struct virgl_winsys *vws,
struct virgl_hw_res *res,
const struct pipe_box *box,
uint32_t stride, uint32_t layer_stride,
uint32_t buf_offset, uint32_t level)
{
struct virgl_vtest_winsys *vtws = virgl_vtest_winsys(vws);
uint32_t size;
void *ptr;
uint32_t valid_stride;
size = vtest_get_transfer_size(res, box, stride, layer_stride, level, &valid_stride);
virgl_vtest_send_transfer_cmd(vtws, VCMD_TRANSFER_GET, res->res_handle,
level, stride, layer_stride,
box, size);
ptr = virgl_vtest_resource_map(vws, res);
virgl_vtest_recv_transfer_get_data(vtws, ptr + buf_offset, size, valid_stride, box, res->format);
virgl_vtest_resource_unmap(vws, res);
return 0;
}
static void virgl_hw_res_destroy(struct virgl_vtest_winsys *vtws,
struct virgl_hw_res *res)
{
virgl_vtest_send_resource_unref(vtws, res->res_handle);
if (res->dt)
vtws->sws->displaytarget_destroy(vtws->sws, res->dt);
free(res->ptr);
FREE(res);
}
static boolean virgl_vtest_resource_is_busy(struct virgl_vtest_winsys *vtws,
struct virgl_hw_res *res)
{
/* implement busy check */
int ret;
ret = virgl_vtest_busy_wait(vtws, res->res_handle, 0);
if (ret < 0)
return FALSE;
return ret == 1 ? TRUE : FALSE;
}
static void
virgl_cache_flush(struct virgl_vtest_winsys *vtws)
{
struct list_head *curr, *next;
struct virgl_hw_res *res;
pipe_mutex_lock(vtws->mutex);
curr = vtws->delayed.next;
next = curr->next;
while (curr != &vtws->delayed) {
res = LIST_ENTRY(struct virgl_hw_res, curr, head);
LIST_DEL(&res->head);
virgl_hw_res_destroy(vtws, res);
curr = next;
next = curr->next;
}
pipe_mutex_unlock(vtws->mutex);
}
static void
virgl_cache_list_check_free(struct virgl_vtest_winsys *vtws)
{
struct list_head *curr, *next;
struct virgl_hw_res *res;
int64_t now;
now = os_time_get();
curr = vtws->delayed.next;
next = curr->next;
while (curr != &vtws->delayed) {
res = LIST_ENTRY(struct virgl_hw_res, curr, head);
if (!os_time_timeout(res->start, res->end, now))
break;
LIST_DEL(&res->head);
virgl_hw_res_destroy(vtws, res);
curr = next;
next = curr->next;
}
}
static void virgl_vtest_resource_reference(struct virgl_vtest_winsys *vtws,
struct virgl_hw_res **dres,
struct virgl_hw_res *sres)
{
struct virgl_hw_res *old = *dres;
if (pipe_reference(&(*dres)->reference, &sres->reference)) {
if (!can_cache_resource(old)) {
virgl_hw_res_destroy(vtws, old);
} else {
pipe_mutex_lock(vtws->mutex);
virgl_cache_list_check_free(vtws);
old->start = os_time_get();
old->end = old->start + vtws->usecs;
LIST_ADDTAIL(&old->head, &vtws->delayed);
vtws->num_delayed++;
pipe_mutex_unlock(vtws->mutex);
}
}
*dres = sres;
}
static struct virgl_hw_res *virgl_vtest_winsys_resource_create(
struct virgl_winsys *vws,
enum pipe_texture_target target,
uint32_t format,
uint32_t bind,
uint32_t width,
uint32_t height,
uint32_t depth,
uint32_t array_size,
uint32_t last_level,
uint32_t nr_samples,
uint32_t size)
{
struct virgl_vtest_winsys *vtws = virgl_vtest_winsys(vws);
struct virgl_hw_res *res;
static int handle = 1;
res = CALLOC_STRUCT(virgl_hw_res);
if (!res)
return NULL;
if (bind & (VIRGL_BIND_DISPLAY_TARGET | VIRGL_BIND_SCANOUT)) {
res->dt = vtws->sws->displaytarget_create(vtws->sws,
bind,
format,
width,
height,
64,
&res->stride);
} else {
res->ptr = align_malloc(size, 64);
if (!res->ptr) {
FREE(res);
return NULL;
}
}
res->bind = bind;
res->format = format;
res->height = height;
res->width = width;
virgl_vtest_send_resource_create(vtws, handle, target, format, bind, width,
height, depth, array_size, last_level,
nr_samples);
res->res_handle = handle++;
pipe_reference_init(&res->reference, 1);
return res;
}
static void virgl_vtest_winsys_resource_unref(struct virgl_winsys *vws,
struct virgl_hw_res *hres)
{
struct virgl_vtest_winsys *vtws = virgl_vtest_winsys(vws);
virgl_vtest_resource_reference(vtws, &hres, NULL);
}
static void *virgl_vtest_resource_map(struct virgl_winsys *vws, struct virgl_hw_res *res)
{
struct virgl_vtest_winsys *vtws = virgl_vtest_winsys(vws);
if (res->dt) {
return vtws->sws->displaytarget_map(vtws->sws, res->dt, 0);
} else {
res->mapped = res->ptr;
return res->mapped;
}
}
static void virgl_vtest_resource_unmap(struct virgl_winsys *vws, struct virgl_hw_res *res)
{
struct virgl_vtest_winsys *vtws = virgl_vtest_winsys(vws);
if (res->mapped)
res->mapped = NULL;
if (res->dt)
vtws->sws->displaytarget_unmap(vtws->sws, res->dt);
}
static void virgl_vtest_resource_wait(struct virgl_winsys *vws, struct virgl_hw_res *res)
{
struct virgl_vtest_winsys *vtws = virgl_vtest_winsys(vws);
virgl_vtest_busy_wait(vtws, res->res_handle, VCMD_BUSY_WAIT_FLAG_WAIT);
}
static inline int virgl_is_res_compat(struct virgl_vtest_winsys *vtws,
struct virgl_hw_res *res,
uint32_t size, uint32_t bind, uint32_t format)
{
if (res->bind != bind)
return 0;
if (res->format != format)
return 0;
if (res->size < size)
return 0;
if (res->size > size * 2)
return 0;
if (virgl_vtest_resource_is_busy(vtws, res)) {
return -1;
}
return 1;
}
static struct virgl_hw_res *virgl_vtest_winsys_resource_cache_create(struct virgl_winsys *vws,
enum pipe_texture_target target,
uint32_t format,
uint32_t bind,
uint32_t width,
uint32_t height,
uint32_t depth,
uint32_t array_size,
uint32_t last_level,
uint32_t nr_samples,
uint32_t size)
{
struct virgl_vtest_winsys *vtws = virgl_vtest_winsys(vws);
struct virgl_hw_res *res, *curr_res;
struct list_head *curr, *next;
int64_t now;
int ret;
/* only store binds for vertex/index/const buffers */
if (bind != VIRGL_BIND_CONSTANT_BUFFER && bind != VIRGL_BIND_INDEX_BUFFER &&
bind != VIRGL_BIND_VERTEX_BUFFER && bind != VIRGL_BIND_CUSTOM)
goto alloc;
pipe_mutex_lock(vtws->mutex);
res = NULL;
curr = vtws->delayed.next;
next = curr->next;
now = os_time_get();
while (curr != &vtws->delayed) {
curr_res = LIST_ENTRY(struct virgl_hw_res, curr, head);
if (!res && (ret = virgl_is_res_compat(vtws, curr_res, size, bind, format) > 0))
res = curr_res;
else if (os_time_timeout(curr_res->start, curr_res->end, now)) {
LIST_DEL(&curr_res->head);
virgl_hw_res_destroy(vtws, curr_res);
} else
break;
if (ret == -1)
break;
curr = next;
next = curr->next;
}
if (!res && ret != -1) {
while (curr != &vtws->delayed) {
curr_res = LIST_ENTRY(struct virgl_hw_res, curr, head);
ret = virgl_is_res_compat(vtws, curr_res, size, bind, format);
if (ret > 0) {
res = curr_res;
break;
}
if (ret == -1)
break;
curr = next;
next = curr->next;
}
}
if (res) {
LIST_DEL(&res->head);
--vtws->num_delayed;
pipe_mutex_unlock(vtws->mutex);
pipe_reference_init(&res->reference, 1);
return res;
}
pipe_mutex_unlock(vtws->mutex);
alloc:
res = virgl_vtest_winsys_resource_create(vws, target, format, bind,
width, height, depth, array_size,
last_level, nr_samples, size);
if (bind == VIRGL_BIND_CONSTANT_BUFFER || bind == VIRGL_BIND_INDEX_BUFFER ||
bind == VIRGL_BIND_VERTEX_BUFFER)
res->cacheable = TRUE;
return res;
}
static struct virgl_cmd_buf *virgl_vtest_cmd_buf_create(struct virgl_winsys *vws)
{
struct virgl_vtest_cmd_buf *cbuf;
cbuf = CALLOC_STRUCT(virgl_vtest_cmd_buf);
if (!cbuf)
return NULL;
cbuf->nres = 512;
cbuf->res_bo = (struct virgl_hw_res **)
CALLOC(cbuf->nres, sizeof(struct virgl_hw_buf*));
if (!cbuf->res_bo) {
FREE(cbuf);
return NULL;
}
cbuf->ws = vws;
cbuf->base.buf = cbuf->buf;
return &cbuf->base;
}
static void virgl_vtest_cmd_buf_destroy(struct virgl_cmd_buf *_cbuf)
{
struct virgl_vtest_cmd_buf *cbuf = (struct virgl_vtest_cmd_buf *)_cbuf;
FREE(cbuf->res_bo);
FREE(cbuf);
}
static boolean virgl_vtest_lookup_res(struct virgl_vtest_cmd_buf *cbuf,
struct virgl_hw_res *res)
{
unsigned hash = res->res_handle & (sizeof(cbuf->is_handle_added)-1);
int i;
if (cbuf->is_handle_added[hash]) {
i = cbuf->reloc_indices_hashlist[hash];
if (cbuf->res_bo[i] == res)
return true;
for (i = 0; i < cbuf->cres; i++) {
if (cbuf->res_bo[i] == res) {
cbuf->reloc_indices_hashlist[hash] = i;
return true;
}
}
}
return false;
}
static void virgl_vtest_release_all_res(struct virgl_vtest_winsys *vtws,
struct virgl_vtest_cmd_buf *cbuf)
{
int i;
for (i = 0; i < cbuf->cres; i++) {
p_atomic_dec(&cbuf->res_bo[i]->num_cs_references);
virgl_vtest_resource_reference(vtws, &cbuf->res_bo[i], NULL);
}
cbuf->cres = 0;
}
static void virgl_vtest_add_res(struct virgl_vtest_winsys *vtws,
struct virgl_vtest_cmd_buf *cbuf, struct virgl_hw_res *res)
{
unsigned hash = res->res_handle & (sizeof(cbuf->is_handle_added)-1);
if (cbuf->cres > cbuf->nres) {
fprintf(stderr,"failure to add relocation\n");
return;
}
cbuf->res_bo[cbuf->cres] = NULL;
virgl_vtest_resource_reference(vtws, &cbuf->res_bo[cbuf->cres], res);
cbuf->is_handle_added[hash] = TRUE;
cbuf->reloc_indices_hashlist[hash] = cbuf->cres;
p_atomic_inc(&res->num_cs_references);
cbuf->cres++;
}
static int virgl_vtest_winsys_submit_cmd(struct virgl_winsys *vws, struct virgl_cmd_buf *_cbuf)
{
struct virgl_vtest_winsys *vtws = virgl_vtest_winsys(vws);
struct virgl_vtest_cmd_buf *cbuf = (struct virgl_vtest_cmd_buf *)_cbuf;
int ret;
if (cbuf->base.cdw == 0)
return 0;
ret = virgl_vtest_submit_cmd(vtws, cbuf);
virgl_vtest_release_all_res(vtws, cbuf);
memset(cbuf->is_handle_added, 0, sizeof(cbuf->is_handle_added));
cbuf->base.cdw = 0;
return ret;
}
static void virgl_vtest_emit_res(struct virgl_winsys *vws, struct virgl_cmd_buf *_cbuf, struct virgl_hw_res *res, boolean write_buf)
{
struct virgl_vtest_winsys *vtws = virgl_vtest_winsys(vws);
struct virgl_vtest_cmd_buf *cbuf = (struct virgl_vtest_cmd_buf *)_cbuf;
boolean already_in_list = virgl_vtest_lookup_res(cbuf, res);
if (write_buf)
cbuf->base.buf[cbuf->base.cdw++] = res->res_handle;
if (!already_in_list)
virgl_vtest_add_res(vtws, cbuf, res);
}
static boolean virgl_vtest_res_is_ref(struct virgl_winsys *vws,
struct virgl_cmd_buf *_cbuf,
struct virgl_hw_res *res)
{
if (!res->num_cs_references)
return FALSE;
return TRUE;
}
static int virgl_vtest_get_caps(struct virgl_winsys *vws, struct virgl_drm_caps *caps)
{
struct virgl_vtest_winsys *vtws = virgl_vtest_winsys(vws);
return virgl_vtest_send_get_caps(vtws, caps);
}
static struct pipe_fence_handle *
virgl_cs_create_fence(struct virgl_winsys *vws)
{
struct virgl_hw_res *res;
res = virgl_vtest_winsys_resource_cache_create(vws,
PIPE_BUFFER,
PIPE_FORMAT_R8_UNORM,
PIPE_BIND_CUSTOM,
8, 1, 1, 0, 0, 0, 8);
return (struct pipe_fence_handle *)res;
}
static bool virgl_fence_wait(struct virgl_winsys *vws,
struct pipe_fence_handle *fence,
uint64_t timeout)
{
struct virgl_vtest_winsys *vdws = virgl_vtest_winsys(vws);
struct virgl_hw_res *res = (struct virgl_hw_res *)fence;
if (timeout == 0)
return virgl_vtest_resource_is_busy(vdws, res);
if (timeout != PIPE_TIMEOUT_INFINITE) {
int64_t start_time = os_time_get();
timeout /= 1000;
while (virgl_vtest_resource_is_busy(vdws, res)) {
if (os_time_get() - start_time >= timeout)
return FALSE;
os_time_sleep(10);
}
return TRUE;
}
virgl_vtest_resource_wait(vws, res);
return TRUE;
}
static void virgl_fence_reference(struct virgl_winsys *vws,
struct pipe_fence_handle **dst,
struct pipe_fence_handle *src)
{
struct virgl_vtest_winsys *vdws = virgl_vtest_winsys(vws);
virgl_vtest_resource_reference(vdws, (struct virgl_hw_res **)dst,
(struct virgl_hw_res *)src);
}
static void virgl_vtest_flush_frontbuffer(struct virgl_winsys *vws,
struct virgl_hw_res *res,
unsigned level, unsigned layer,
void *winsys_drawable_handle,
struct pipe_box *sub_box)
{
struct virgl_vtest_winsys *vtws = virgl_vtest_winsys(vws);
struct pipe_box box;
void *map;
uint32_t size;
uint32_t offset = 0, valid_stride;
if (!res->dt)
return;
memset(&box, 0, sizeof(box));
if (sub_box) {
box = *sub_box;
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);
} else {
box.z = layer;
box.width = res->width;
box.height = res->height;
box.depth = 1;
}
size = vtest_get_transfer_size(res, &box, res->stride, 0, level, &valid_stride);
virgl_vtest_busy_wait(vtws, res->res_handle, VCMD_BUSY_WAIT_FLAG_WAIT);
map = vtws->sws->displaytarget_map(vtws->sws, res->dt, 0);
/* execute a transfer */
virgl_vtest_send_transfer_cmd(vtws, VCMD_TRANSFER_GET, res->res_handle,
level, res->stride, 0, &box, size);
virgl_vtest_recv_transfer_get_data(vtws, map + offset, size, valid_stride, &box, res->format);
vtws->sws->displaytarget_unmap(vtws->sws, res->dt);
vtws->sws->displaytarget_display(vtws->sws, res->dt, winsys_drawable_handle, sub_box);
}
static void
virgl_vtest_winsys_destroy(struct virgl_winsys *vws)
{
struct virgl_vtest_winsys *vtws = virgl_vtest_winsys(vws);
virgl_cache_flush(vtws);
pipe_mutex_destroy(vtws->mutex);
FREE(vtws);
}
struct virgl_winsys *
virgl_vtest_winsys_wrap(struct sw_winsys *sws)
{
struct virgl_vtest_winsys *vtws;
vtws = CALLOC_STRUCT(virgl_vtest_winsys);
if (!vtws)
return NULL;
virgl_vtest_connect(vtws);
vtws->sws = sws;
vtws->usecs = 1000000;
LIST_INITHEAD(&vtws->delayed);
pipe_mutex_init(vtws->mutex);
vtws->base.destroy = virgl_vtest_winsys_destroy;
vtws->base.transfer_put = virgl_vtest_transfer_put;
vtws->base.transfer_get = virgl_vtest_transfer_get;
vtws->base.resource_create = virgl_vtest_winsys_resource_cache_create;
vtws->base.resource_unref = virgl_vtest_winsys_resource_unref;
vtws->base.resource_map = virgl_vtest_resource_map;
vtws->base.resource_wait = virgl_vtest_resource_wait;
vtws->base.cmd_buf_create = virgl_vtest_cmd_buf_create;
vtws->base.cmd_buf_destroy = virgl_vtest_cmd_buf_destroy;
vtws->base.submit_cmd = virgl_vtest_winsys_submit_cmd;
vtws->base.emit_res = virgl_vtest_emit_res;
vtws->base.res_is_referenced = virgl_vtest_res_is_ref;
vtws->base.get_caps = virgl_vtest_get_caps;
vtws->base.cs_create_fence = virgl_cs_create_fence;
vtws->base.fence_wait = virgl_fence_wait;
vtws->base.fence_reference = virgl_fence_reference;
vtws->base.flush_frontbuffer = virgl_vtest_flush_frontbuffer;
return &vtws->base;
}

View file

@ -0,0 +1,132 @@
/*
* Copyright 2014, 2015 Red Hat.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* on the rights to use, copy, modify, merge, publish, distribute, sub
* license, and/or sell copies of the Software, and to permit persons to whom
* the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
* THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
* USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef VIRGL_DRM_WINSYS_H
#define VIRGL_DRM_WINSYS_H
#include <stdint.h>
#include "pipe/p_compiler.h"
#include "pipe/p_defines.h"
#include "pipe/p_state.h"
#include "state_tracker/sw_winsys.h"
#include "../drm/virgl_hw.h"
#include "virgl/virgl_winsys.h"
#include "util/list.h"
#include "os/os_thread.h"
#include "vtest_protocol.h"
struct virgl_vtest_winsys {
struct virgl_winsys base;
struct sw_winsys *sws;
/* fd to remote renderer */
int sock_fd;
struct list_head delayed;
int num_delayed;
unsigned usecs;
pipe_mutex mutex;
};
struct virgl_hw_res {
struct pipe_reference reference;
uint32_t res_handle;
int num_cs_references;
void *ptr;
int size;
uint32_t format;
uint32_t stride;
uint32_t width;
uint32_t height;
struct sw_displaytarget *dt;
void *mapped;
struct list_head head;
uint32_t bind;
boolean cacheable;
int64_t start, end;
};
struct virgl_vtest_cmd_buf {
struct virgl_cmd_buf base;
uint32_t buf[VIRGL_MAX_CMDBUF_DWORDS];
unsigned nres;
unsigned cres;
struct virgl_winsys *ws;
struct virgl_hw_res **res_bo;
char is_handle_added[512];
unsigned reloc_indices_hashlist[512];
};
static inline struct virgl_vtest_winsys *
virgl_vtest_winsys(struct virgl_winsys *iws)
{
return (struct virgl_vtest_winsys *)iws;
}
int virgl_vtest_connect(struct virgl_vtest_winsys *vws);
int virgl_vtest_send_get_caps(struct virgl_vtest_winsys *vws,
struct virgl_drm_caps *caps);
int virgl_vtest_send_resource_create(struct virgl_vtest_winsys *vws,
uint32_t handle,
enum pipe_texture_target target,
uint32_t format,
uint32_t bind,
uint32_t width,
uint32_t height,
uint32_t depth,
uint32_t array_size,
uint32_t last_level,
uint32_t nr_samples);
int virgl_vtest_send_resource_unref(struct virgl_vtest_winsys *vws,
uint32_t handle);
int virgl_vtest_submit_cmd(struct virgl_vtest_winsys *vtws,
struct virgl_vtest_cmd_buf *cbuf);
int virgl_vtest_send_transfer_cmd(struct virgl_vtest_winsys *vws,
uint32_t vcmd,
uint32_t handle,
uint32_t level, uint32_t stride,
uint32_t layer_stride,
const struct pipe_box *box,
uint32_t data_size);
int virgl_vtest_send_transfer_put_data(struct virgl_vtest_winsys *vws,
void *data,
uint32_t data_size);
int virgl_vtest_recv_transfer_get_data(struct virgl_vtest_winsys *vws,
void *data,
uint32_t data_size,
uint32_t stride,
const struct pipe_box *box, uint32_t format);
int virgl_vtest_busy_wait(struct virgl_vtest_winsys *vws, int handle,
int flags);
#endif

View file

@ -0,0 +1,88 @@
/*
* Copyright 2014, 2015 Red Hat.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* on the rights to use, copy, modify, merge, publish, distribute, sub
* license, and/or sell copies of the Software, and to permit persons to whom
* the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
* THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
* USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef VTEST_PROTOCOL
#define VTEST_PROTOCOL
#define VTEST_DEFAULT_SOCKET_NAME "/tmp/.virgl_test"
/* 32-bit length field */
/* 32-bit cmd field */
#define VTEST_HDR_SIZE 2
#define VTEST_CMD_LEN 0 /* length of data */
#define VTEST_CMD_ID 1
#define VTEST_CMD_DATA_START 2
/* vtest cmds */
#define VCMD_GET_CAPS 1
#define VCMD_RESOURCE_CREATE 2
#define VCMD_RESOURCE_UNREF 3
#define VCMD_TRANSFER_GET 4
#define VCMD_TRANSFER_PUT 5
#define VCMD_SUBMIT_CMD 6
#define VCMD_RESOURCE_BUSY_WAIT 7
/* pass the process cmd line for debugging */
#define VCMD_CREATE_RENDERER 8
/* get caps */
/* 0 length cmd */
/* resp VCMD_GET_CAPS + caps */
#define VCMD_RES_CREATE_SIZE 10
#define VCMD_RES_CREATE_RES_HANDLE 0
#define VCMD_RES_CREATE_TARGET 1
#define VCMD_RES_CREATE_FORMAT 2
#define VCMD_RES_CREATE_BIND 3
#define VCMD_RES_CREATE_WIDTH 4
#define VCMD_RES_CREATE_HEIGHT 5
#define VCMD_RES_CREATE_DEPTH 6
#define VCMD_RES_CREATE_ARRAY_SIZE 7
#define VCMD_RES_CREATE_LAST_LEVEL 8
#define VCMD_RES_CREATE_NR_SAMPLES 9
#define VCMD_RES_UNREF_SIZE 1
#define VCMD_RES_UNREF_RES_HANDLE 0
#define VCMD_TRANSFER_HDR_SIZE 11
#define VCMD_TRANSFER_RES_HANDLE 0
#define VCMD_TRANSFER_LEVEL 1
#define VCMD_TRANSFER_STRIDE 2
#define VCMD_TRANSFER_LAYER_STRIDE 3
#define VCMD_TRANSFER_X 4
#define VCMD_TRANSFER_Y 5
#define VCMD_TRANSFER_Z 6
#define VCMD_TRANSFER_WIDTH 7
#define VCMD_TRANSFER_HEIGHT 8
#define VCMD_TRANSFER_DEPTH 9
#define VCMD_TRANSFER_DATA_SIZE 10
#define VCMD_BUSY_WAIT_FLAG_WAIT 1
#define VCMD_BUSY_WAIT_SIZE 2
#define VCMD_BUSY_WAIT_HANDLE 0
#define VCMD_BUSY_WAIT_FLAGS 1
#endif