svga/winsys: implement GBS support

This is a squash commit of many commits by Thomas Hellstrom.

Reviewed-by: Thomas Hellstrom <thellstrom@vmware.com>
Cc: "10.1" <mesa-stable@lists.freedesktop.org>
This commit is contained in:
Brian Paul 2014-02-08 09:51:15 -08:00
parent 59e7c59621
commit fe6a854477
19 changed files with 3084 additions and 343 deletions

View file

@ -7,4 +7,6 @@ C_SOURCES := \
vmw_screen_ioctl.c \
vmw_screen_pools.c \
vmw_screen_svga.c \
vmw_surface.c
vmw_surface.c \
vmw_shader.c \
pb_buffer_simple_fenced.c

View file

@ -0,0 +1,844 @@
/**************************************************************************
*
* Copyright 2007-2010 VMware, Inc.
* All Rights Reserved.
*
* 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, 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 VMWARE AND/OR ITS 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.
*
**************************************************************************/
/**
* \file
* Implementation of fenced buffers.
*
* \author Jose Fonseca <jfonseca-at-vmware-dot-com>
* \author Thomas Hellström <thellstrom-at-vmware-dot-com>
*/
#include "pipe/p_config.h"
#if defined(PIPE_OS_LINUX) || defined(PIPE_OS_BSD) || defined(PIPE_OS_SOLARIS)
#include <unistd.h>
#include <sched.h>
#endif
#include "pipe/p_compiler.h"
#include "pipe/p_defines.h"
#include "util/u_debug.h"
#include "os/os_thread.h"
#include "util/u_memory.h"
#include "util/u_double_list.h"
#include "pipebuffer/pb_buffer.h"
#include "pipebuffer/pb_bufmgr.h"
#include "pipebuffer/pb_buffer_fenced.h"
#include "vmw_screen.h"
/**
* Convenience macro (type safe).
*/
#define SUPER(__derived) (&(__derived)->base)
struct fenced_manager
{
struct pb_manager base;
struct pb_manager *provider;
struct pb_fence_ops *ops;
/**
* Following members are mutable and protected by this mutex.
*/
pipe_mutex mutex;
/**
* Fenced buffer list.
*
* All fenced buffers are placed in this listed, ordered from the oldest
* fence to the newest fence.
*/
struct list_head fenced;
pb_size num_fenced;
struct list_head unfenced;
pb_size num_unfenced;
};
/**
* Fenced buffer.
*
* Wrapper around a pipe buffer which adds fencing and reference counting.
*/
struct fenced_buffer
{
/*
* Immutable members.
*/
struct pb_buffer base;
struct fenced_manager *mgr;
/*
* Following members are mutable and protected by fenced_manager::mutex.
*/
struct list_head head;
/**
* Buffer with storage.
*/
struct pb_buffer *buffer;
pb_size size;
/**
* A bitmask of PB_USAGE_CPU/GPU_READ/WRITE describing the current
* buffer usage.
*/
unsigned flags;
unsigned mapcount;
struct pb_validate *vl;
unsigned validation_flags;
struct pipe_fence_handle *fence;
};
static INLINE struct fenced_manager *
fenced_manager(struct pb_manager *mgr)
{
assert(mgr);
return (struct fenced_manager *)mgr;
}
static INLINE struct fenced_buffer *
fenced_buffer(struct pb_buffer *buf)
{
assert(buf);
return (struct fenced_buffer *)buf;
}
static void
fenced_buffer_destroy_gpu_storage_locked(struct fenced_buffer *fenced_buf);
static enum pipe_error
fenced_buffer_create_gpu_storage_locked(struct fenced_manager *fenced_mgr,
struct fenced_buffer *fenced_buf,
const struct pb_desc *desc,
boolean wait);
/**
* Dump the fenced buffer list.
*
* Useful to understand failures to allocate buffers.
*/
static void
fenced_manager_dump_locked(struct fenced_manager *fenced_mgr)
{
#ifdef DEBUG
struct pb_fence_ops *ops = fenced_mgr->ops;
struct list_head *curr, *next;
struct fenced_buffer *fenced_buf;
debug_printf("%10s %7s %8s %7s %10s %s\n",
"buffer", "size", "refcount", "storage", "fence", "signalled");
curr = fenced_mgr->unfenced.next;
next = curr->next;
while(curr != &fenced_mgr->unfenced) {
fenced_buf = LIST_ENTRY(struct fenced_buffer, curr, head);
assert(!fenced_buf->fence);
debug_printf("%10p %7u %8u %7s\n",
(void *) fenced_buf,
fenced_buf->base.size,
p_atomic_read(&fenced_buf->base.reference.count),
fenced_buf->buffer ? "gpu" : "none");
curr = next;
next = curr->next;
}
curr = fenced_mgr->fenced.next;
next = curr->next;
while(curr != &fenced_mgr->fenced) {
int signaled;
fenced_buf = LIST_ENTRY(struct fenced_buffer, curr, head);
assert(fenced_buf->buffer);
signaled = ops->fence_signalled(ops, fenced_buf->fence, 0);
debug_printf("%10p %7u %8u %7s %10p %s\n",
(void *) fenced_buf,
fenced_buf->base.size,
p_atomic_read(&fenced_buf->base.reference.count),
"gpu",
(void *) fenced_buf->fence,
signaled == 0 ? "y" : "n");
curr = next;
next = curr->next;
}
#else
(void)fenced_mgr;
#endif
}
static INLINE void
fenced_buffer_destroy_locked(struct fenced_manager *fenced_mgr,
struct fenced_buffer *fenced_buf)
{
assert(!pipe_is_referenced(&fenced_buf->base.reference));
assert(!fenced_buf->fence);
assert(fenced_buf->head.prev);
assert(fenced_buf->head.next);
LIST_DEL(&fenced_buf->head);
assert(fenced_mgr->num_unfenced);
--fenced_mgr->num_unfenced;
fenced_buffer_destroy_gpu_storage_locked(fenced_buf);
FREE(fenced_buf);
}
/**
* Add the buffer to the fenced list.
*
* Reference count should be incremented before calling this function.
*/
static INLINE void
fenced_buffer_add_locked(struct fenced_manager *fenced_mgr,
struct fenced_buffer *fenced_buf)
{
assert(pipe_is_referenced(&fenced_buf->base.reference));
assert(fenced_buf->flags & PB_USAGE_GPU_READ_WRITE);
assert(fenced_buf->fence);
p_atomic_inc(&fenced_buf->base.reference.count);
LIST_DEL(&fenced_buf->head);
assert(fenced_mgr->num_unfenced);
--fenced_mgr->num_unfenced;
LIST_ADDTAIL(&fenced_buf->head, &fenced_mgr->fenced);
++fenced_mgr->num_fenced;
}
/**
* Remove the buffer from the fenced list, and potentially destroy the buffer
* if the reference count reaches zero.
*
* Returns TRUE if the buffer was detroyed.
*/
static INLINE boolean
fenced_buffer_remove_locked(struct fenced_manager *fenced_mgr,
struct fenced_buffer *fenced_buf)
{
struct pb_fence_ops *ops = fenced_mgr->ops;
assert(fenced_buf->fence);
assert(fenced_buf->mgr == fenced_mgr);
ops->fence_reference(ops, &fenced_buf->fence, NULL);
fenced_buf->flags &= ~PB_USAGE_GPU_READ_WRITE;
assert(fenced_buf->head.prev);
assert(fenced_buf->head.next);
LIST_DEL(&fenced_buf->head);
assert(fenced_mgr->num_fenced);
--fenced_mgr->num_fenced;
LIST_ADDTAIL(&fenced_buf->head, &fenced_mgr->unfenced);
++fenced_mgr->num_unfenced;
if (p_atomic_dec_zero(&fenced_buf->base.reference.count)) {
fenced_buffer_destroy_locked(fenced_mgr, fenced_buf);
return TRUE;
}
return FALSE;
}
/**
* Wait for the fence to expire, and remove it from the fenced list.
*
* This function will release and re-aquire the mutex, so any copy of mutable
* state must be discarded after calling it.
*/
static INLINE enum pipe_error
fenced_buffer_finish_locked(struct fenced_manager *fenced_mgr,
struct fenced_buffer *fenced_buf)
{
struct pb_fence_ops *ops = fenced_mgr->ops;
enum pipe_error ret = PIPE_ERROR;
#if 0
debug_warning("waiting for GPU");
#endif
assert(pipe_is_referenced(&fenced_buf->base.reference));
assert(fenced_buf->fence);
if(fenced_buf->fence) {
struct pipe_fence_handle *fence = NULL;
int finished;
boolean proceed;
ops->fence_reference(ops, &fence, fenced_buf->fence);
pipe_mutex_unlock(fenced_mgr->mutex);
finished = ops->fence_finish(ops, fenced_buf->fence, 0);
pipe_mutex_lock(fenced_mgr->mutex);
assert(pipe_is_referenced(&fenced_buf->base.reference));
/*
* Only proceed if the fence object didn't change in the meanwhile.
* Otherwise assume the work has been already carried out by another
* thread that re-aquired the lock before us.
*/
proceed = fence == fenced_buf->fence ? TRUE : FALSE;
ops->fence_reference(ops, &fence, NULL);
if(proceed && finished == 0) {
/*
* Remove from the fenced list
*/
boolean destroyed;
destroyed = fenced_buffer_remove_locked(fenced_mgr, fenced_buf);
/* TODO: remove consequents buffers with the same fence? */
assert(!destroyed);
fenced_buf->flags &= ~PB_USAGE_GPU_READ_WRITE;
ret = PIPE_OK;
}
}
return ret;
}
/**
* Remove as many fenced buffers from the fenced list as possible.
*
* Returns TRUE if at least one buffer was removed.
*/
static boolean
fenced_manager_check_signalled_locked(struct fenced_manager *fenced_mgr,
boolean wait)
{
struct pb_fence_ops *ops = fenced_mgr->ops;
struct list_head *curr, *next;
struct fenced_buffer *fenced_buf;
struct pipe_fence_handle *prev_fence = NULL;
boolean ret = FALSE;
curr = fenced_mgr->fenced.next;
next = curr->next;
while(curr != &fenced_mgr->fenced) {
fenced_buf = LIST_ENTRY(struct fenced_buffer, curr, head);
if(fenced_buf->fence != prev_fence) {
int signaled;
if (wait) {
signaled = ops->fence_finish(ops, fenced_buf->fence, 0);
/*
* Don't return just now. Instead preemptively check if the
* following buffers' fences already expired,
* without further waits.
*/
wait = FALSE;
}
else {
signaled = ops->fence_signalled(ops, fenced_buf->fence, 0);
}
if (signaled != 0) {
return ret;
}
prev_fence = fenced_buf->fence;
}
else {
/* This buffer's fence object is identical to the previous buffer's
* fence object, so no need to check the fence again.
*/
assert(ops->fence_signalled(ops, fenced_buf->fence, 0) == 0);
}
fenced_buffer_remove_locked(fenced_mgr, fenced_buf);
ret = TRUE;
curr = next;
next = curr->next;
}
return ret;
}
/**
* Destroy the GPU storage.
*/
static void
fenced_buffer_destroy_gpu_storage_locked(struct fenced_buffer *fenced_buf)
{
if(fenced_buf->buffer) {
pb_reference(&fenced_buf->buffer, NULL);
}
}
/**
* Try to create GPU storage for this buffer.
*
* This function is a shorthand around pb_manager::create_buffer for
* fenced_buffer_create_gpu_storage_locked()'s benefit.
*/
static INLINE boolean
fenced_buffer_try_create_gpu_storage_locked(struct fenced_manager *fenced_mgr,
struct fenced_buffer *fenced_buf,
const struct pb_desc *desc)
{
struct pb_manager *provider = fenced_mgr->provider;
assert(!fenced_buf->buffer);
fenced_buf->buffer = provider->create_buffer(fenced_mgr->provider,
fenced_buf->size, desc);
return fenced_buf->buffer ? TRUE : FALSE;
}
/**
* Create GPU storage for this buffer.
*/
static enum pipe_error
fenced_buffer_create_gpu_storage_locked(struct fenced_manager *fenced_mgr,
struct fenced_buffer *fenced_buf,
const struct pb_desc *desc,
boolean wait)
{
assert(!fenced_buf->buffer);
/*
* Check for signaled buffers before trying to allocate.
*/
fenced_manager_check_signalled_locked(fenced_mgr, FALSE);
fenced_buffer_try_create_gpu_storage_locked(fenced_mgr, fenced_buf, desc);
/*
* Keep trying while there is some sort of progress:
* - fences are expiring,
* - or buffers are being being swapped out from GPU memory into CPU memory.
*/
while(!fenced_buf->buffer &&
(fenced_manager_check_signalled_locked(fenced_mgr, FALSE))) {
fenced_buffer_try_create_gpu_storage_locked(fenced_mgr, fenced_buf,
desc);
}
if(!fenced_buf->buffer && wait) {
/*
* Same as before, but this time around, wait to free buffers if
* necessary.
*/
while(!fenced_buf->buffer &&
(fenced_manager_check_signalled_locked(fenced_mgr, TRUE))) {
fenced_buffer_try_create_gpu_storage_locked(fenced_mgr, fenced_buf,
desc);
}
}
if(!fenced_buf->buffer) {
if(0)
fenced_manager_dump_locked(fenced_mgr);
/* give up */
return PIPE_ERROR_OUT_OF_MEMORY;
}
return PIPE_OK;
}
static void
fenced_buffer_destroy(struct pb_buffer *buf)
{
struct fenced_buffer *fenced_buf = fenced_buffer(buf);
struct fenced_manager *fenced_mgr = fenced_buf->mgr;
assert(!pipe_is_referenced(&fenced_buf->base.reference));
pipe_mutex_lock(fenced_mgr->mutex);
fenced_buffer_destroy_locked(fenced_mgr, fenced_buf);
pipe_mutex_unlock(fenced_mgr->mutex);
}
static void *
fenced_buffer_map(struct pb_buffer *buf,
unsigned flags, void *flush_ctx)
{
struct fenced_buffer *fenced_buf = fenced_buffer(buf);
struct fenced_manager *fenced_mgr = fenced_buf->mgr;
struct pb_fence_ops *ops = fenced_mgr->ops;
void *map = NULL;
pipe_mutex_lock(fenced_mgr->mutex);
assert(!(flags & PB_USAGE_GPU_READ_WRITE));
/*
* Serialize writes.
*/
while((fenced_buf->flags & PB_USAGE_GPU_WRITE) ||
((fenced_buf->flags & PB_USAGE_GPU_READ) &&
(flags & PB_USAGE_CPU_WRITE))) {
/*
* Don't wait for the GPU to finish accessing it,
* if blocking is forbidden.
*/
if((flags & PB_USAGE_DONTBLOCK) &&
ops->fence_signalled(ops, fenced_buf->fence, 0) != 0) {
goto done;
}
if (flags & PB_USAGE_UNSYNCHRONIZED) {
break;
}
/*
* Wait for the GPU to finish accessing. This will release and re-acquire
* the mutex, so all copies of mutable state must be discarded.
*/
fenced_buffer_finish_locked(fenced_mgr, fenced_buf);
}
map = pb_map(fenced_buf->buffer, flags, flush_ctx);
if(map) {
++fenced_buf->mapcount;
fenced_buf->flags |= flags & PB_USAGE_CPU_READ_WRITE;
}
done:
pipe_mutex_unlock(fenced_mgr->mutex);
return map;
}
static void
fenced_buffer_unmap(struct pb_buffer *buf)
{
struct fenced_buffer *fenced_buf = fenced_buffer(buf);
struct fenced_manager *fenced_mgr = fenced_buf->mgr;
pipe_mutex_lock(fenced_mgr->mutex);
assert(fenced_buf->mapcount);
if(fenced_buf->mapcount) {
if (fenced_buf->buffer)
pb_unmap(fenced_buf->buffer);
--fenced_buf->mapcount;
if(!fenced_buf->mapcount)
fenced_buf->flags &= ~PB_USAGE_CPU_READ_WRITE;
}
pipe_mutex_unlock(fenced_mgr->mutex);
}
static enum pipe_error
fenced_buffer_validate(struct pb_buffer *buf,
struct pb_validate *vl,
unsigned flags)
{
struct fenced_buffer *fenced_buf = fenced_buffer(buf);
struct fenced_manager *fenced_mgr = fenced_buf->mgr;
enum pipe_error ret;
pipe_mutex_lock(fenced_mgr->mutex);
if(!vl) {
/* invalidate */
fenced_buf->vl = NULL;
fenced_buf->validation_flags = 0;
ret = PIPE_OK;
goto done;
}
assert(flags & PB_USAGE_GPU_READ_WRITE);
assert(!(flags & ~PB_USAGE_GPU_READ_WRITE));
flags &= PB_USAGE_GPU_READ_WRITE;
/* Buffer cannot be validated in two different lists */
if(fenced_buf->vl && fenced_buf->vl != vl) {
ret = PIPE_ERROR_RETRY;
goto done;
}
if(fenced_buf->vl == vl &&
(fenced_buf->validation_flags & flags) == flags) {
/* Nothing to do -- buffer already validated */
ret = PIPE_OK;
goto done;
}
ret = pb_validate(fenced_buf->buffer, vl, flags);
if (ret != PIPE_OK)
goto done;
fenced_buf->vl = vl;
fenced_buf->validation_flags |= flags;
done:
pipe_mutex_unlock(fenced_mgr->mutex);
return ret;
}
static void
fenced_buffer_fence(struct pb_buffer *buf,
struct pipe_fence_handle *fence)
{
struct fenced_buffer *fenced_buf = fenced_buffer(buf);
struct fenced_manager *fenced_mgr = fenced_buf->mgr;
struct pb_fence_ops *ops = fenced_mgr->ops;
pipe_mutex_lock(fenced_mgr->mutex);
assert(pipe_is_referenced(&fenced_buf->base.reference));
assert(fenced_buf->buffer);
if(fence != fenced_buf->fence) {
assert(fenced_buf->vl);
assert(fenced_buf->validation_flags);
if (fenced_buf->fence) {
boolean destroyed;
destroyed = fenced_buffer_remove_locked(fenced_mgr, fenced_buf);
assert(!destroyed);
}
if (fence) {
ops->fence_reference(ops, &fenced_buf->fence, fence);
fenced_buf->flags |= fenced_buf->validation_flags;
fenced_buffer_add_locked(fenced_mgr, fenced_buf);
}
pb_fence(fenced_buf->buffer, fence);
fenced_buf->vl = NULL;
fenced_buf->validation_flags = 0;
}
pipe_mutex_unlock(fenced_mgr->mutex);
}
static void
fenced_buffer_get_base_buffer(struct pb_buffer *buf,
struct pb_buffer **base_buf,
pb_size *offset)
{
struct fenced_buffer *fenced_buf = fenced_buffer(buf);
struct fenced_manager *fenced_mgr = fenced_buf->mgr;
pipe_mutex_lock(fenced_mgr->mutex);
assert(fenced_buf->buffer);
if(fenced_buf->buffer)
pb_get_base_buffer(fenced_buf->buffer, base_buf, offset);
else {
*base_buf = buf;
*offset = 0;
}
pipe_mutex_unlock(fenced_mgr->mutex);
}
static const struct pb_vtbl
fenced_buffer_vtbl = {
fenced_buffer_destroy,
fenced_buffer_map,
fenced_buffer_unmap,
fenced_buffer_validate,
fenced_buffer_fence,
fenced_buffer_get_base_buffer
};
/**
* Wrap a buffer in a fenced buffer.
*/
static struct pb_buffer *
fenced_bufmgr_create_buffer(struct pb_manager *mgr,
pb_size size,
const struct pb_desc *desc)
{
struct fenced_manager *fenced_mgr = fenced_manager(mgr);
struct fenced_buffer *fenced_buf;
enum pipe_error ret;
fenced_buf = CALLOC_STRUCT(fenced_buffer);
if(!fenced_buf)
goto no_buffer;
pipe_reference_init(&fenced_buf->base.reference, 1);
fenced_buf->base.alignment = desc->alignment;
fenced_buf->base.usage = desc->usage;
fenced_buf->base.size = size;
fenced_buf->size = size;
fenced_buf->base.vtbl = &fenced_buffer_vtbl;
fenced_buf->mgr = fenced_mgr;
pipe_mutex_lock(fenced_mgr->mutex);
/*
* Try to create GPU storage without stalling,
*/
ret = fenced_buffer_create_gpu_storage_locked(fenced_mgr, fenced_buf,
desc, TRUE);
/*
* Give up.
*/
if(ret != PIPE_OK) {
goto no_storage;
}
assert(fenced_buf->buffer);
LIST_ADDTAIL(&fenced_buf->head, &fenced_mgr->unfenced);
++fenced_mgr->num_unfenced;
pipe_mutex_unlock(fenced_mgr->mutex);
return &fenced_buf->base;
no_storage:
pipe_mutex_unlock(fenced_mgr->mutex);
FREE(fenced_buf);
no_buffer:
return NULL;
}
static void
fenced_bufmgr_flush(struct pb_manager *mgr)
{
struct fenced_manager *fenced_mgr = fenced_manager(mgr);
pipe_mutex_lock(fenced_mgr->mutex);
while(fenced_manager_check_signalled_locked(fenced_mgr, TRUE))
;
pipe_mutex_unlock(fenced_mgr->mutex);
assert(fenced_mgr->provider->flush);
if(fenced_mgr->provider->flush)
fenced_mgr->provider->flush(fenced_mgr->provider);
}
static void
fenced_bufmgr_destroy(struct pb_manager *mgr)
{
struct fenced_manager *fenced_mgr = fenced_manager(mgr);
pipe_mutex_lock(fenced_mgr->mutex);
/* Wait on outstanding fences */
while (fenced_mgr->num_fenced) {
pipe_mutex_unlock(fenced_mgr->mutex);
#if defined(PIPE_OS_LINUX) || defined(PIPE_OS_BSD) || defined(PIPE_OS_SOLARIS)
sched_yield();
#endif
pipe_mutex_lock(fenced_mgr->mutex);
while(fenced_manager_check_signalled_locked(fenced_mgr, TRUE))
;
}
#ifdef DEBUG
/*assert(!fenced_mgr->num_unfenced);*/
#endif
pipe_mutex_unlock(fenced_mgr->mutex);
pipe_mutex_destroy(fenced_mgr->mutex);
FREE(fenced_mgr);
}
struct pb_manager *
simple_fenced_bufmgr_create(struct pb_manager *provider,
struct pb_fence_ops *ops)
{
struct fenced_manager *fenced_mgr;
if(!provider)
return NULL;
fenced_mgr = CALLOC_STRUCT(fenced_manager);
if (!fenced_mgr)
return NULL;
fenced_mgr->base.destroy = fenced_bufmgr_destroy;
fenced_mgr->base.create_buffer = fenced_bufmgr_create_buffer;
fenced_mgr->base.flush = fenced_bufmgr_flush;
fenced_mgr->provider = provider;
fenced_mgr->ops = ops;
LIST_INITHEAD(&fenced_mgr->fenced);
fenced_mgr->num_fenced = 0;
LIST_INITHEAD(&fenced_mgr->unfenced);
fenced_mgr->num_unfenced = 0;
pipe_mutex_init(fenced_mgr->mutex);
return &fenced_mgr->base;
}

View file

@ -51,7 +51,6 @@
#include "vmw_screen.h"
#include "vmw_buffer.h"
struct vmw_gmr_bufmgr;
@ -63,6 +62,7 @@ struct vmw_gmr_buffer
struct vmw_region *region;
void *map;
unsigned map_flags;
};
@ -113,6 +113,25 @@ vmw_gmr_buffer_map(struct pb_buffer *_buf,
void *flush_ctx)
{
struct vmw_gmr_buffer *buf = vmw_gmr_buffer(_buf);
int ret;
if (!buf->map)
buf->map = vmw_ioctl_region_map(buf->region);
if (!buf->map)
return NULL;
if ((_buf->usage & VMW_BUFFER_USAGE_SYNC) &&
!(flags & PB_USAGE_UNSYNCHRONIZED)) {
ret = vmw_ioctl_syncforcpu(buf->region,
!!(flags & PB_USAGE_DONTBLOCK),
!(flags & PB_USAGE_CPU_WRITE),
FALSE);
if (ret)
return NULL;
}
return buf->map;
}
@ -120,8 +139,15 @@ vmw_gmr_buffer_map(struct pb_buffer *_buf,
static void
vmw_gmr_buffer_unmap(struct pb_buffer *_buf)
{
/* Do nothing */
(void)_buf;
struct vmw_gmr_buffer *buf = vmw_gmr_buffer(_buf);
unsigned flags = buf->map_flags;
if ((_buf->usage & VMW_BUFFER_USAGE_SYNC) &&
!(flags & PB_USAGE_UNSYNCHRONIZED)) {
vmw_ioctl_releasefromcpu(buf->region,
!(flags & PB_USAGE_CPU_WRITE),
FALSE);
}
}
@ -167,35 +193,33 @@ const struct pb_vtbl vmw_gmr_buffer_vtbl = {
static struct pb_buffer *
vmw_gmr_bufmgr_create_buffer(struct pb_manager *_mgr,
pb_size size,
const struct pb_desc *desc)
const struct pb_desc *pb_desc)
{
struct vmw_gmr_bufmgr *mgr = vmw_gmr_bufmgr(_mgr);
struct vmw_winsys_screen *vws = mgr->vws;
struct vmw_gmr_buffer *buf;
const struct vmw_buffer_desc *desc =
(const struct vmw_buffer_desc *) pb_desc;
buf = CALLOC_STRUCT(vmw_gmr_buffer);
if(!buf)
goto error1;
pipe_reference_init(&buf->base.reference, 1);
buf->base.alignment = desc->alignment;
buf->base.usage = desc->usage;
buf->base.size = size;
buf->base.alignment = pb_desc->alignment;
buf->base.usage = pb_desc->usage & ~VMW_BUFFER_USAGE_SHARED;
buf->base.vtbl = &vmw_gmr_buffer_vtbl;
buf->mgr = mgr;
buf->region = vmw_ioctl_region_create(vws, size);
if(!buf->region)
goto error2;
buf->base.size = size;
if ((pb_desc->usage & VMW_BUFFER_USAGE_SHARED) && desc->region) {
buf->region = desc->region;
} else {
buf->region = vmw_ioctl_region_create(vws, size);
if(!buf->region)
goto error2;
}
buf->map = vmw_ioctl_region_map(buf->region);
if(!buf->map)
goto error3;
return &buf->base;
error3:
vmw_ioctl_region_destroy(buf->region);
error2:
FREE(buf);
error1:
@ -257,3 +281,91 @@ vmw_gmr_bufmgr_region_ptr(struct pb_buffer *buf,
return TRUE;
}
#ifdef DEBUG
struct svga_winsys_buffer {
struct pb_buffer *pb_buf;
struct debug_flush_buf *fbuf;
};
struct pb_buffer *
vmw_pb_buffer(struct svga_winsys_buffer *buffer)
{
assert(buffer);
return buffer->pb_buf;
}
struct svga_winsys_buffer *
vmw_svga_winsys_buffer_wrap(struct pb_buffer *buffer)
{
struct svga_winsys_buffer *buf;
if (!buffer)
return NULL;
buf = CALLOC_STRUCT(svga_winsys_buffer);
if (!buf) {
pb_reference(&buffer, NULL);
return NULL;
}
buf->pb_buf = buffer;
buf->fbuf = debug_flush_buf_create(TRUE, VMW_DEBUG_FLUSH_STACK);
return buf;
}
struct debug_flush_buf *
vmw_debug_flush_buf(struct svga_winsys_buffer *buffer)
{
return buffer->fbuf;
}
#endif
void
vmw_svga_winsys_buffer_destroy(struct svga_winsys_screen *sws,
struct svga_winsys_buffer *buf)
{
struct pb_buffer *pbuf = vmw_pb_buffer(buf);
(void)sws;
pb_reference(&pbuf, NULL);
#ifdef DEBUG
debug_flush_buf_reference(&buf->fbuf, NULL);
FREE(buf);
#endif
}
void *
vmw_svga_winsys_buffer_map(struct svga_winsys_screen *sws,
struct svga_winsys_buffer *buf,
unsigned flags)
{
void *map;
(void)sws;
if (flags & PIPE_TRANSFER_UNSYNCHRONIZED)
flags &= ~PIPE_TRANSFER_DONTBLOCK;
map = pb_map(vmw_pb_buffer(buf), flags, NULL);
#ifdef DEBUG
if (map != NULL)
debug_flush_map(buf->fbuf, flags);
#endif
return map;
}
void
vmw_svga_winsys_buffer_unmap(struct svga_winsys_screen *sws,
struct svga_winsys_buffer *buf)
{
(void)sws;
#ifdef DEBUG
debug_flush_unmap(buf->fbuf);
#endif
pb_unmap(vmw_pb_buffer(buf));
}

View file

@ -29,6 +29,12 @@
#include <assert.h>
#include "pipe/p_compiler.h"
#include "pipebuffer/pb_bufmgr.h"
#include "util/u_debug_flush.h"
#define VMW_BUFFER_USAGE_SHARED (1 << 20)
#define VMW_BUFFER_USAGE_SYNC (1 << 21)
struct SVGAGuestPtr;
struct pb_buffer;
@ -37,7 +43,22 @@ struct svga_winsys_buffer;
struct svga_winsys_surface;
struct vmw_winsys_screen;
struct vmw_buffer_desc {
struct pb_desc pb_desc;
struct vmw_region *region;
};
#ifdef DEBUG
struct pb_buffer *
vmw_pb_buffer(struct svga_winsys_buffer *buffer);
struct svga_winsys_buffer *
vmw_svga_winsys_buffer_wrap(struct pb_buffer *buffer);
struct debug_flush_buf *
vmw_debug_flush_buf(struct svga_winsys_buffer *buffer);
#else
static INLINE struct pb_buffer *
vmw_pb_buffer(struct svga_winsys_buffer *buffer)
{
@ -47,12 +68,23 @@ vmw_pb_buffer(struct svga_winsys_buffer *buffer)
static INLINE struct svga_winsys_buffer *
vmw_svga_winsys_buffer(struct pb_buffer *buffer)
vmw_svga_winsys_buffer_wrap(struct pb_buffer *buffer)
{
assert(buffer);
return (struct svga_winsys_buffer *)buffer;
}
#endif
void
vmw_svga_winsys_buffer_destroy(struct svga_winsys_screen *sws,
struct svga_winsys_buffer *buf);
void *
vmw_svga_winsys_buffer_map(struct svga_winsys_screen *sws,
struct svga_winsys_buffer *buf,
unsigned flags);
void
vmw_svga_winsys_buffer_unmap(struct svga_winsys_screen *sws,
struct svga_winsys_buffer *buf);
struct pb_manager *
vmw_gmr_bufmgr_create(struct vmw_winsys_screen *vws);

View file

@ -29,6 +29,8 @@
#include "util/u_debug.h"
#include "util/u_memory.h"
#include "util/u_debug_stack.h"
#include "util/u_debug_flush.h"
#include "util/u_hash_table.h"
#include "pipebuffer/pb_buffer.h"
#include "pipebuffer/pb_validate.h"
@ -38,19 +40,38 @@
#include "vmw_buffer.h"
#include "vmw_surface.h"
#include "vmw_fence.h"
#include "vmw_shader.h"
#define VMW_COMMAND_SIZE (64*1024)
#define VMW_SURFACE_RELOCS (1024)
#define VMW_SHADER_RELOCS (1024)
#define VMW_REGION_RELOCS (512)
#define VMW_MUST_FLUSH_STACK 8
struct vmw_region_relocation
struct vmw_buffer_relocation
{
struct SVGAGuestPtr *where;
struct pb_buffer *buffer;
/* TODO: put offset info inside where */
boolean is_mob;
uint32 offset;
union {
struct {
struct SVGAGuestPtr *where;
} region;
struct {
SVGAMobId *id;
uint32 *offset_into_mob;
} mob;
};
};
struct vmw_ctx_validate_item {
union {
struct vmw_svga_winsys_surface *vsurf;
struct vmw_svga_winsys_shader *vshader;
};
boolean referenced;
};
struct vmw_svga_winsys_context
@ -58,10 +79,12 @@ struct vmw_svga_winsys_context
struct svga_winsys_context base;
struct vmw_winsys_screen *vws;
struct util_hash_table *hash;
#ifdef DEBUG
boolean must_flush;
struct debug_stack_frame must_flush_stack[VMW_MUST_FLUSH_STACK];
struct debug_flush_ctx *fctx;
#endif
struct {
@ -72,7 +95,7 @@ struct vmw_svga_winsys_context
} command;
struct {
struct vmw_svga_winsys_surface *handles[VMW_SURFACE_RELOCS];
struct vmw_ctx_validate_item items[VMW_SURFACE_RELOCS];
uint32_t size;
uint32_t used;
uint32_t staged;
@ -80,20 +103,30 @@ struct vmw_svga_winsys_context
} surface;
struct {
struct vmw_region_relocation relocs[VMW_REGION_RELOCS];
struct vmw_buffer_relocation relocs[VMW_REGION_RELOCS];
uint32_t size;
uint32_t used;
uint32_t staged;
uint32_t reserved;
} region;
struct {
struct vmw_ctx_validate_item items[VMW_SHADER_RELOCS];
uint32_t size;
uint32_t used;
uint32_t staged;
uint32_t reserved;
} shader;
struct pb_validate *validate;
/**
* The amount of GMR that is referred by the commands currently batched
* in the context.
* The amount of surface, GMR or MOB memory that is referred by the commands
* currently batched in the context command buffer.
*/
uint32_t seen_regions;
uint64_t seen_surfaces;
uint64_t seen_regions;
uint64_t seen_mobs;
/**
* Whether this context should fail to reserve more commands, not because it
@ -140,7 +173,7 @@ vmw_swc_flush(struct svga_winsys_context *swc,
/* Apply relocations */
for(i = 0; i < vswc->region.used; ++i) {
struct vmw_region_relocation *reloc = &vswc->region.relocs[i];
struct vmw_buffer_relocation *reloc = &vswc->region.relocs[i];
struct SVGAGuestPtr ptr;
if(!vmw_gmr_bufmgr_region_ptr(reloc->buffer, &ptr))
@ -148,7 +181,16 @@ vmw_swc_flush(struct svga_winsys_context *swc,
ptr.offset += reloc->offset;
*reloc->where = ptr;
if (reloc->is_mob) {
if (reloc->mob.id)
*reloc->mob.id = ptr.gmrId;
if (reloc->mob.offset_into_mob)
*reloc->mob.offset_into_mob = ptr.offset;
else {
assert(ptr.offset == 0);
}
} else
*reloc->region.where = ptr;
}
if (vswc->command.used || pfence != NULL)
@ -166,27 +208,37 @@ vmw_swc_flush(struct svga_winsys_context *swc,
vswc->command.reserved = 0;
for(i = 0; i < vswc->surface.used + vswc->surface.staged; ++i) {
struct vmw_svga_winsys_surface *vsurf =
vswc->surface.handles[i];
p_atomic_dec(&vsurf->validated);
vmw_svga_winsys_surface_reference(&vswc->surface.handles[i], NULL);
struct vmw_ctx_validate_item *isurf = &vswc->surface.items[i];
if (isurf->referenced)
p_atomic_dec(&isurf->vsurf->validated);
vmw_svga_winsys_surface_reference(&isurf->vsurf, NULL);
}
util_hash_table_clear(vswc->hash);
vswc->surface.used = 0;
vswc->surface.reserved = 0;
for(i = 0; i < vswc->region.used + vswc->region.staged; ++i) {
pb_reference(&vswc->region.relocs[i].buffer, NULL);
for(i = 0; i < vswc->shader.used + vswc->shader.staged; ++i) {
struct vmw_ctx_validate_item *ishader = &vswc->shader.items[i];
if (ishader->referenced)
p_atomic_dec(&ishader->vshader->validated);
vmw_svga_winsys_shader_reference(&ishader->vshader, NULL);
}
vswc->shader.used = 0;
vswc->shader.reserved = 0;
vswc->region.used = 0;
vswc->region.reserved = 0;
#ifdef DEBUG
vswc->must_flush = FALSE;
debug_flush_flush(vswc->fctx);
#endif
vswc->preemptive_flush = FALSE;
vswc->seen_surfaces = 0;
vswc->seen_regions = 0;
vswc->seen_mobs = 0;
if(pfence)
vmw_fence_reference(vswc->vws, pfence, fence);
@ -210,6 +262,7 @@ vmw_swc_reserve(struct svga_winsys_context *swc,
debug_backtrace_dump(vswc->must_flush_stack, VMW_MUST_FLUSH_STACK);
assert(!vswc->must_flush);
}
debug_flush_might_flush(vswc->fctx);
#endif
assert(nr_bytes <= vswc->command.size);
@ -219,6 +272,7 @@ vmw_swc_reserve(struct svga_winsys_context *swc,
if(vswc->preemptive_flush ||
vswc->command.used + nr_bytes > vswc->command.size ||
vswc->surface.used + nr_relocs > vswc->surface.size ||
vswc->shader.used + nr_relocs > vswc->shader.size ||
vswc->region.used + nr_relocs > vswc->region.size) {
#ifdef DEBUG
vswc->must_flush = TRUE;
@ -230,44 +284,51 @@ vmw_swc_reserve(struct svga_winsys_context *swc,
assert(vswc->command.used + nr_bytes <= vswc->command.size);
assert(vswc->surface.used + nr_relocs <= vswc->surface.size);
assert(vswc->shader.used + nr_relocs <= vswc->shader.size);
assert(vswc->region.used + nr_relocs <= vswc->region.size);
vswc->command.reserved = nr_bytes;
vswc->surface.reserved = nr_relocs;
vswc->surface.staged = 0;
vswc->shader.reserved = nr_relocs;
vswc->shader.staged = 0;
vswc->region.reserved = nr_relocs;
vswc->region.staged = 0;
return vswc->command.buffer + vswc->command.used;
}
static void
vmw_swc_surface_relocation(struct svga_winsys_context *swc,
uint32 *where,
uint32 *mobid,
struct svga_winsys_surface *surface,
unsigned flags)
vmw_swc_context_relocation(struct svga_winsys_context *swc,
uint32 *cid)
{
struct vmw_svga_winsys_context *vswc = vmw_svga_winsys_context(swc);
struct vmw_svga_winsys_surface *vsurf;
if(!surface) {
*where = SVGA3D_INVALID_ID;
return;
}
assert(vswc->surface.staged < vswc->surface.reserved);
vsurf = vmw_svga_winsys_surface(surface);
*where = vsurf->sid;
vmw_svga_winsys_surface_reference(&vswc->surface.handles[vswc->surface.used + vswc->surface.staged], vsurf);
p_atomic_inc(&vsurf->validated);
++vswc->surface.staged;
*cid = swc->cid;
}
static boolean
vmw_swc_add_validate_buffer(struct vmw_svga_winsys_context *vswc,
struct pb_buffer *pb_buf,
unsigned flags)
{
enum pipe_error ret;
unsigned translated_flags;
/*
* TODO: Update pb_validate to provide a similar functionality
* (Check buffer already present before adding)
*/
if (util_hash_table_get(vswc->hash, pb_buf) != pb_buf) {
translated_flags = vmw_translate_to_pb_flags(flags);
ret = pb_validate_add_buffer(vswc->validate, pb_buf, translated_flags);
/* TODO: Update pipebuffer to reserve buffers and not fail here */
assert(ret == PIPE_OK);
(void)ret;
(void)util_hash_table_set(vswc->hash, pb_buf, pb_buf);
return TRUE;
}
return FALSE;
}
static void
vmw_swc_region_relocation(struct svga_winsys_context *swc,
@ -277,46 +338,216 @@ vmw_swc_region_relocation(struct svga_winsys_context *swc,
unsigned flags)
{
struct vmw_svga_winsys_context *vswc = vmw_svga_winsys_context(swc);
struct vmw_region_relocation *reloc;
unsigned translated_flags;
enum pipe_error ret;
struct vmw_buffer_relocation *reloc;
assert(vswc->region.staged < vswc->region.reserved);
reloc = &vswc->region.relocs[vswc->region.used + vswc->region.staged];
reloc->where = where;
pb_reference(&reloc->buffer, vmw_pb_buffer(buffer));
reloc->offset = offset;
++vswc->region.staged;
translated_flags = vmw_translate_to_pb_flags(flags);
ret = pb_validate_add_buffer(vswc->validate, reloc->buffer, translated_flags);
/* TODO: Update pipebuffer to reserve buffers and not fail here */
assert(ret == PIPE_OK);
(void)ret;
reloc->region.where = where;
/*
* Flush preemptively the FIFO commands to keep the GMR working set within
* the GMR pool size.
*
* This is necessary for applications like SPECviewperf that generate huge
* amounts of immediate vertex data, so that we don't pile up too much of
* that vertex data neither in the guest nor in the host.
*
* Note that in the current implementation if a region is referred twice in
* a command stream, it will be accounted twice. We could detect repeated
* regions and count only once, but there is no incentive to do that, since
* regions are typically short-lived; always referred in a single command;
* and at the worst we just flush the commands a bit sooner, which for the
* SVGA virtual device it's not a performance issue since flushing commands
* to the FIFO won't cause flushing in the host.
* pb_validate holds a refcount to the buffer, so no need to
* refcount it again in the relocation.
*/
vswc->seen_regions += reloc->buffer->size;
if(vswc->seen_regions >= VMW_GMR_POOL_SIZE/3)
vswc->preemptive_flush = TRUE;
reloc->buffer = vmw_pb_buffer(buffer);
reloc->offset = offset;
reloc->is_mob = FALSE;
++vswc->region.staged;
if (vmw_swc_add_validate_buffer(vswc, reloc->buffer, flags)) {
vswc->seen_regions += reloc->buffer->size;
if(vswc->seen_regions >= VMW_GMR_POOL_SIZE/5)
vswc->preemptive_flush = TRUE;
}
#ifdef DEBUG
if (!(flags & SVGA_RELOC_INTERNAL))
debug_flush_cb_reference(vswc->fctx, vmw_debug_flush_buf(buffer));
#endif
}
static void
vmw_swc_mob_relocation(struct svga_winsys_context *swc,
SVGAMobId *id,
uint32 *offset_into_mob,
struct svga_winsys_buffer *buffer,
uint32 offset,
unsigned flags)
{
struct vmw_svga_winsys_context *vswc = vmw_svga_winsys_context(swc);
struct vmw_buffer_relocation *reloc;
assert(vswc->region.staged < vswc->region.reserved);
reloc = &vswc->region.relocs[vswc->region.used + vswc->region.staged];
reloc->mob.id = id;
reloc->mob.offset_into_mob = offset_into_mob;
/*
* pb_validate holds a refcount to the buffer, so no need to
* refcount it again in the relocation.
*/
reloc->buffer = vmw_pb_buffer(buffer);
reloc->offset = offset;
reloc->is_mob = TRUE;
++vswc->region.staged;
if (vmw_swc_add_validate_buffer(vswc, reloc->buffer, flags)) {
vswc->seen_mobs += reloc->buffer->size;
/* divide by 5, tested for best performance */
if (vswc->seen_mobs >= vswc->vws->ioctl.max_mob_memory / 5)
vswc->preemptive_flush = TRUE;
}
#ifdef DEBUG
if (!(flags & SVGA_RELOC_INTERNAL))
debug_flush_cb_reference(vswc->fctx, vmw_debug_flush_buf(buffer));
#endif
}
/**
* vmw_swc_surface_clear_reference - Clear referenced info for a surface
*
* @swc: Pointer to an svga_winsys_context
* @vsurf: Pointer to a vmw_svga_winsys_surface, the referenced info of which
* we want to clear
*
* This is primarily used by a discard surface map to indicate that the
* surface data is no longer referenced by a draw call, and mapping it
* should therefore no longer cause a flush.
*/
void
vmw_swc_surface_clear_reference(struct svga_winsys_context *swc,
struct vmw_svga_winsys_surface *vsurf)
{
struct vmw_svga_winsys_context *vswc = vmw_svga_winsys_context(swc);
struct vmw_ctx_validate_item *isrf =
util_hash_table_get(vswc->hash, vsurf);
if (isrf && isrf->referenced) {
isrf->referenced = FALSE;
p_atomic_dec(&vsurf->validated);
}
}
static void
vmw_swc_surface_only_relocation(struct svga_winsys_context *swc,
uint32 *where,
struct vmw_svga_winsys_surface *vsurf,
unsigned flags)
{
struct vmw_svga_winsys_context *vswc = vmw_svga_winsys_context(swc);
struct vmw_ctx_validate_item *isrf;
assert(vswc->surface.staged < vswc->surface.reserved);
isrf = util_hash_table_get(vswc->hash, vsurf);
if (isrf == NULL) {
isrf = &vswc->surface.items[vswc->surface.used + vswc->surface.staged];
vmw_svga_winsys_surface_reference(&isrf->vsurf, vsurf);
isrf->referenced = FALSE;
/*
* Note that a failure here may just fall back to unhashed behavior
* and potentially cause unnecessary flushing, so ignore the
* return code.
*/
(void) util_hash_table_set(vswc->hash, vsurf, isrf);
++vswc->surface.staged;
vswc->seen_surfaces += vsurf->size;
/* divide by 5 not well tuned for performance */
if (vswc->seen_surfaces >= vswc->vws->ioctl.max_surface_memory / 5)
vswc->preemptive_flush = TRUE;
}
if (!(flags & SVGA_RELOC_INTERNAL) && !isrf->referenced) {
isrf->referenced = TRUE;
p_atomic_inc(&vsurf->validated);
}
*where = vsurf->sid;
}
static void
vmw_swc_surface_relocation(struct svga_winsys_context *swc,
uint32 *where,
uint32 *mobid,
struct svga_winsys_surface *surface,
unsigned flags)
{
struct vmw_svga_winsys_surface *vsurf;
assert(swc->have_gb_objects || mobid == NULL);
if(!surface) {
*where = SVGA3D_INVALID_ID;
if (mobid)
*mobid = SVGA3D_INVALID_ID;
return;
}
vsurf = vmw_svga_winsys_surface(surface);
vmw_swc_surface_only_relocation(swc, where, vsurf, flags);
if (swc->have_gb_objects && vsurf->buf != NULL) {
/*
* Make sure backup buffer ends up fenced.
*/
pipe_mutex_lock(vsurf->mutex);
assert(vsurf->buf != NULL);
vmw_swc_mob_relocation(swc, mobid, NULL, (struct svga_winsys_buffer *)
vsurf->buf, 0, flags);
pipe_mutex_unlock(vsurf->mutex);
}
}
static void
vmw_swc_shader_relocation(struct svga_winsys_context *swc,
uint32 *shid,
uint32 *mobid,
uint32 *offset,
struct svga_winsys_gb_shader *shader)
{
struct vmw_svga_winsys_context *vswc = vmw_svga_winsys_context(swc);
struct vmw_svga_winsys_shader *vshader;
struct vmw_ctx_validate_item *ishader;
if(!shader) {
*shid = SVGA3D_INVALID_ID;
return;
}
assert(vswc->shader.staged < vswc->shader.reserved);
vshader = vmw_svga_winsys_shader(shader);
ishader = util_hash_table_get(vswc->hash, vshader);
if (ishader == NULL) {
ishader = &vswc->shader.items[vswc->shader.used + vswc->shader.staged];
vmw_svga_winsys_shader_reference(&ishader->vshader, vshader);
ishader->referenced = FALSE;
/*
* Note that a failure here may just fall back to unhashed behavior
* and potentially cause unnecessary flushing, so ignore the
* return code.
*/
(void) util_hash_table_set(vswc->hash, vshader, ishader);
++vswc->shader.staged;
}
if (!ishader->referenced) {
ishader->referenced = TRUE;
p_atomic_inc(&vshader->validated);
}
*shid = vshader->shid;
if (mobid != NULL && vshader->buf)
vmw_swc_mob_relocation(swc, mobid, offset, vshader->buf,
0, SVGA_RELOC_READ);
}
static void
vmw_swc_commit(struct svga_winsys_context *swc)
@ -334,6 +565,12 @@ vmw_swc_commit(struct svga_winsys_context *swc)
vswc->surface.staged = 0;
vswc->surface.reserved = 0;
assert(vswc->shader.staged <= vswc->shader.reserved);
assert(vswc->shader.used + vswc->shader.staged <= vswc->shader.size);
vswc->shader.used += vswc->shader.staged;
vswc->shader.staged = 0;
vswc->shader.reserved = 0;
assert(vswc->region.staged <= vswc->region.reserved);
assert(vswc->region.used + vswc->region.staged <= vswc->region.size);
vswc->region.used += vswc->region.staged;
@ -348,19 +585,38 @@ vmw_swc_destroy(struct svga_winsys_context *swc)
struct vmw_svga_winsys_context *vswc = vmw_svga_winsys_context(swc);
unsigned i;
for(i = 0; i < vswc->region.used; ++i) {
pb_reference(&vswc->region.relocs[i].buffer, NULL);
for(i = 0; i < vswc->surface.used; ++i) {
struct vmw_ctx_validate_item *isurf = &vswc->surface.items[i];
if (isurf->referenced)
p_atomic_dec(&isurf->vsurf->validated);
vmw_svga_winsys_surface_reference(&isurf->vsurf, NULL);
}
for(i = 0; i < vswc->surface.used; ++i) {
p_atomic_dec(&vswc->surface.handles[i]->validated);
vmw_svga_winsys_surface_reference(&vswc->surface.handles[i], NULL);
for(i = 0; i < vswc->shader.used; ++i) {
struct vmw_ctx_validate_item *ishader = &vswc->shader.items[i];
if (ishader->referenced)
p_atomic_dec(&ishader->vshader->validated);
vmw_svga_winsys_shader_reference(&ishader->vshader, NULL);
}
util_hash_table_destroy(vswc->hash);
pb_validate_destroy(vswc->validate);
vmw_ioctl_context_destroy(vswc->vws, swc->cid);
#ifdef DEBUG
debug_flush_ctx_destroy(vswc->fctx);
#endif
FREE(vswc);
}
static unsigned vmw_hash_ptr(void *p)
{
return (unsigned)(unsigned long)p;
}
static int vmw_ptr_compare(void *key1, void *key2)
{
return (key1 == key2) ? 0 : 1;
}
struct svga_winsys_context *
vmw_svga_winsys_context_create(struct svga_winsys_screen *sws)
@ -376,22 +632,41 @@ vmw_svga_winsys_context_create(struct svga_winsys_screen *sws)
vswc->base.reserve = vmw_swc_reserve;
vswc->base.surface_relocation = vmw_swc_surface_relocation;
vswc->base.region_relocation = vmw_swc_region_relocation;
vswc->base.mob_relocation = vmw_swc_mob_relocation;
vswc->base.context_relocation = vmw_swc_context_relocation;
vswc->base.shader_relocation = vmw_swc_shader_relocation;
vswc->base.commit = vmw_swc_commit;
vswc->base.flush = vmw_swc_flush;
vswc->base.surface_map = vmw_svga_winsys_surface_map;
vswc->base.surface_unmap = vmw_svga_winsys_surface_unmap;
vswc->base.cid = vmw_ioctl_context_create(vws);
vswc->base.have_gb_objects = sws->have_gb_objects;
vswc->vws = vws;
vswc->command.size = VMW_COMMAND_SIZE;
vswc->surface.size = VMW_SURFACE_RELOCS;
vswc->shader.size = VMW_SHADER_RELOCS;
vswc->region.size = VMW_REGION_RELOCS;
vswc->validate = pb_validate_create();
if(!vswc->validate) {
FREE(vswc);
return NULL;
}
if(!vswc->validate)
goto out_no_validate;
vswc->hash = util_hash_table_create(vmw_hash_ptr, vmw_ptr_compare);
if (!vswc->hash)
goto out_no_hash;
#ifdef DEBUG
vswc->fctx = debug_flush_ctx_create(TRUE, VMW_DEBUG_FLUSH_STACK);
#endif
return &vswc->base;
out_no_hash:
pb_validate_destroy(vswc->validate);
out_no_validate:
FREE(vswc);
return NULL;
}

View file

@ -62,4 +62,12 @@ struct pipe_screen;
struct svga_winsys_context *
vmw_svga_winsys_context_create(struct svga_winsys_screen *sws);
struct vmw_svga_winsys_surface;
void
vmw_swc_surface_clear_reference(struct svga_winsys_context *swc,
struct vmw_svga_winsys_surface *vsurf);
#endif /* VMW_CONTEXT_H_ */

View file

@ -22,20 +22,10 @@
* SOFTWARE.
*
**********************************************************/
/*
* TODO:
*
* Fencing is currently a bit inefficient, since we need to call the
* kernel do determine a fence object signaled status if the fence is not
* signaled. This can be greatly improved upon by using the fact that the
* execbuf ioctl returns the last signaled fence seqno, as does the
* fence signaled ioctl. We should set up a ring of fence objects and
* walk through them checking for signaled status each time we receive a
* new passed fence seqno.
*/
#include "util/u_memory.h"
#include "util/u_atomic.h"
#include "util/u_double_list.h"
#include "os/os_thread.h"
#include "pipebuffer/pb_buffer_fenced.h"
@ -44,54 +34,45 @@
struct vmw_fence_ops
{
/*
* Immutable members.
*/
struct pb_fence_ops base;
struct vmw_winsys_screen *vws;
pipe_mutex mutex;
/*
* Protected by mutex;
*/
struct list_head not_signaled;
uint32_t last_signaled;
uint32_t last_emitted;
};
struct vmw_fence
{
struct list_head ops_list;
int32_t refcount;
uint32_t handle;
uint32_t mask;
int32_t signalled;
uint32_t seqno;
};
/**
* vmw_fence - return the vmw_fence object identified by a
* struct pipe_fence_handle *
* vmw_fence_seq_is_signaled - Check whether a fence seqno is
* signaled.
*
* @ops: Pointer to a struct pb_fence_ops.
*
* @fence: The opaque pipe fence handle.
*/
static INLINE struct vmw_fence *
vmw_fence(struct pipe_fence_handle *fence)
static INLINE boolean
vmw_fence_seq_is_signaled(uint32_t seq, uint32_t last, uint32_t cur)
{
return (struct vmw_fence *) fence;
return (cur - last <= cur - seq);
}
/**
* vmw_fence_create - Create a user-space fence object.
*
* @handle: Handle identifying the kernel fence object.
* @mask: Mask of flags that this fence object may signal.
*
* Returns NULL on failure.
*/
struct pipe_fence_handle *
vmw_fence_create(uint32_t handle, uint32_t mask)
{
struct vmw_fence *fence = CALLOC_STRUCT(vmw_fence);
if (!fence)
return NULL;
p_atomic_set(&fence->refcount, 1);
fence->handle = handle;
fence->mask = mask;
p_atomic_set(&fence->signalled, 0);
return (struct pipe_fence_handle *) fence;
}
/**
* vmw_fence_ops - Return the vmw_fence_ops structure backing a
@ -108,6 +89,125 @@ vmw_fence_ops(struct pb_fence_ops *ops)
}
/**
* vmw_fences_release - Release all fences from the not_signaled
* list.
*
* @ops: Pointer to a struct vmw_fence_ops.
*
*/
static void
vmw_fences_release(struct vmw_fence_ops *ops)
{
struct vmw_fence *fence, *n;
pipe_mutex_lock(ops->mutex);
LIST_FOR_EACH_ENTRY_SAFE(fence, n, &ops->not_signaled, ops_list)
LIST_DELINIT(&fence->ops_list);
pipe_mutex_unlock(ops->mutex);
}
/**
* vmw_fences_signal - Traverse the not_signaled list and try to
* signal unsignaled fences.
*
* @ops: Pointer to a struct pb_fence_ops.
* @signaled: Seqno that has signaled.
* @emitted: Last seqno emitted by the kernel.
* @has_emitted: Whether we provide the emitted value.
*
*/
void
vmw_fences_signal(struct pb_fence_ops *fence_ops,
uint32_t signaled,
uint32_t emitted,
boolean has_emitted)
{
struct vmw_fence_ops *ops = NULL;
struct vmw_fence *fence, *n;
if (fence_ops == NULL)
return;
ops = vmw_fence_ops(fence_ops);
pipe_mutex_lock(ops->mutex);
if (!has_emitted) {
emitted = ops->last_emitted;
if (emitted - signaled > (1 << 30))
emitted = signaled;
}
if (signaled == ops->last_signaled && emitted == ops->last_emitted)
goto out_unlock;
LIST_FOR_EACH_ENTRY_SAFE(fence, n, &ops->not_signaled, ops_list) {
if (!vmw_fence_seq_is_signaled(fence->seqno, signaled, emitted))
break;
p_atomic_set(&fence->signalled, 1);
LIST_DELINIT(&fence->ops_list);
}
ops->last_signaled = signaled;
ops->last_emitted = emitted;
out_unlock:
pipe_mutex_unlock(ops->mutex);
}
/**
* vmw_fence - return the vmw_fence object identified by a
* struct pipe_fence_handle *
*
* @fence: The opaque pipe fence handle.
*/
static INLINE struct vmw_fence *
vmw_fence(struct pipe_fence_handle *fence)
{
return (struct vmw_fence *) fence;
}
/**
* vmw_fence_create - Create a user-space fence object.
*
* @fence_ops: The fence_ops manager to register with.
* @handle: Handle identifying the kernel fence object.
* @mask: Mask of flags that this fence object may signal.
*
* Returns NULL on failure.
*/
struct pipe_fence_handle *
vmw_fence_create(struct pb_fence_ops *fence_ops, uint32_t handle,
uint32_t seqno, uint32_t mask)
{
struct vmw_fence *fence = CALLOC_STRUCT(vmw_fence);
struct vmw_fence_ops *ops = vmw_fence_ops(fence_ops);
if (!fence)
return NULL;
p_atomic_set(&fence->refcount, 1);
fence->handle = handle;
fence->mask = mask;
fence->seqno = seqno;
p_atomic_set(&fence->signalled, 0);
pipe_mutex_lock(ops->mutex);
if (vmw_fence_seq_is_signaled(seqno, ops->last_signaled, seqno)) {
p_atomic_set(&fence->signalled, 1);
LIST_INITHEAD(&fence->ops_list);
} else {
p_atomic_set(&fence->signalled, 0);
LIST_ADDTAIL(&fence->ops_list, &ops->not_signaled);
}
pipe_mutex_unlock(ops->mutex);
return (struct pipe_fence_handle *) fence;
}
/**
* vmw_fence_reference - Reference / unreference a vmw fence object.
@ -125,7 +225,14 @@ vmw_fence_reference(struct vmw_winsys_screen *vws,
struct vmw_fence *vfence = vmw_fence(*ptr);
if (p_atomic_dec_zero(&vfence->refcount)) {
struct vmw_fence_ops *ops = vmw_fence_ops(vws->fence_ops);
vmw_ioctl_fence_unref(vws, vfence->handle);
pipe_mutex_lock(ops->mutex);
LIST_DELINIT(&vfence->ops_list);
pipe_mutex_unlock(ops->mutex);
FREE(vfence);
}
}
@ -171,18 +278,21 @@ vmw_fence_signalled(struct vmw_winsys_screen *vws,
if ((old & vflags) == vflags)
return 0;
/*
* Currently we update signaled fences on each execbuf call.
* That should really be sufficient, and we can avoid
* a lot of kernel calls this way.
*/
#if 1
ret = vmw_ioctl_fence_signalled(vws, vfence->handle, vflags);
if (ret == 0) {
int32_t prev = old;
do {
old = prev;
prev = p_atomic_cmpxchg(&vfence->signalled, old, old | vflags);
} while (prev != old);
}
if (ret == 0)
p_atomic_set(&vfence->signalled, 1);
return ret;
#else
(void) ret;
return -1;
#endif
}
/**
@ -287,6 +397,7 @@ vmw_fence_ops_fence_finish(struct pb_fence_ops *ops,
static void
vmw_fence_ops_destroy(struct pb_fence_ops *ops)
{
vmw_fences_release(vmw_fence_ops(ops));
FREE(ops);
}
@ -310,6 +421,8 @@ vmw_fence_ops_create(struct vmw_winsys_screen *vws)
if(!ops)
return NULL;
pipe_mutex_init(ops->mutex);
LIST_INITHEAD(&ops->not_signaled);
ops->base.destroy = &vmw_fence_ops_destroy;
ops->base.fence_reference = &vmw_fence_ops_fence_reference;
ops->base.fence_signalled = &vmw_fence_ops_fence_signalled;
@ -319,5 +432,3 @@ vmw_fence_ops_create(struct vmw_winsys_screen *vws)
return &ops->base;
}

View file

@ -29,7 +29,7 @@
#include "pipe/p_compiler.h"
#include "pipebuffer/pb_buffer_fenced.h"
struct pipe_fence_handle;
struct pb_fence_ops;
@ -37,7 +37,8 @@ struct vmw_winsys_screen;
struct pipe_fence_handle *
vmw_fence_create(uint32_t handle, uint32_t mask);
vmw_fence_create(struct pb_fence_ops *fence_ops,
uint32_t handle, uint32_t seqno, uint32_t mask);
int
vmw_fence_finish(struct vmw_winsys_screen *vws,

View file

@ -25,42 +25,94 @@
#include "vmw_screen.h"
#include "vmw_fence.h"
#include "vmw_context.h"
#include "util/u_memory.h"
#include "pipe/p_compiler.h"
#include "util/u_hash_table.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
static struct util_hash_table *dev_hash = NULL;
static int vmw_dev_compare(void *key1, void *key2)
{
return (major(*(dev_t *)key1) == major(*(dev_t *)key2) &&
minor(*(dev_t *)key1) == minor(*(dev_t *)key2)) ? 0 : 1;
}
static unsigned vmw_dev_hash(void *key)
{
return (major(*(dev_t *) key) << 16) | minor(*(dev_t *) key);
}
/* Called from vmw_drm_create_screen(), creates and initializes the
* vmw_winsys_screen structure, which is the main entity in this
* module.
* First, check whether a vmw_winsys_screen object already exists for
* this device, and in that case return that one, making sure that we
* have our own file descriptor open to DRM.
*/
struct vmw_winsys_screen *
vmw_winsys_create( int fd, boolean use_old_scanout_flag )
{
struct vmw_winsys_screen *vws = CALLOC_STRUCT(vmw_winsys_screen);
struct vmw_winsys_screen *vws;
struct stat stat_buf;
if (dev_hash == NULL) {
dev_hash = util_hash_table_create(vmw_dev_hash, vmw_dev_compare);
if (dev_hash == NULL)
return NULL;
}
if (fstat(fd, &stat_buf))
return NULL;
vws = util_hash_table_get(dev_hash, &stat_buf.st_rdev);
if (vws) {
vws->open_count++;
return vws;
}
vws = CALLOC_STRUCT(vmw_winsys_screen);
if (!vws)
goto out_no_vws;
vws->ioctl.drm_fd = fd;
vws->device = stat_buf.st_rdev;
vws->open_count = 1;
vws->ioctl.drm_fd = dup(fd);
vws->use_old_scanout_flag = use_old_scanout_flag;
vws->base.have_gb_dma = TRUE;
if (!vmw_ioctl_init(vws))
goto out_no_ioctl;
vws->fence_ops = vmw_fence_ops_create(vws);
if (!vws->fence_ops)
goto out_no_fence_ops;
if(!vmw_pools_init(vws))
goto out_no_pools;
if (!vmw_winsys_screen_init_svga(vws))
goto out_no_svga;
if (util_hash_table_set(dev_hash, &vws->device, vws) != PIPE_OK)
goto out_no_hash_insert;
return vws;
out_no_hash_insert:
out_no_svga:
vmw_pools_cleanup(vws);
out_no_pools:
vws->fence_ops->destroy(vws->fence_ops);
out_no_fence_ops:
vmw_ioctl_cleanup(vws);
out_no_ioctl:
close(vws->ioctl.drm_fd);
FREE(vws);
out_no_vws:
return NULL;
@ -69,7 +121,12 @@ out_no_vws:
void
vmw_winsys_destroy(struct vmw_winsys_screen *vws)
{
vmw_pools_cleanup(vws);
vmw_ioctl_cleanup(vws);
FREE(vws);
if (--vws->open_count == 0) {
util_hash_table_remove(dev_hash, &vws->device);
vmw_pools_cleanup(vws);
vws->fence_ops->destroy(vws->fence_ops);
vmw_ioctl_cleanup(vws);
close(vws->ioctl.drm_fd);
FREE(vws);
}
}

View file

@ -39,10 +39,13 @@
#include "pipe/p_state.h"
#include "svga_winsys.h"
#include "pipebuffer/pb_buffer_fenced.h"
#define VMW_GMR_POOL_SIZE (16*1024*1024)
#define VMW_QUERY_POOL_SIZE (8192)
#define VMW_DEBUG_FLUSH_STACK 10
/*
* Something big, but arbitrary. The kernel reports an error if it can't
* handle this, and the svga driver will resort to multiple partial
@ -53,6 +56,10 @@
struct pb_manager;
struct vmw_region;
struct vmw_cap_3d {
boolean has_cap;
SVGA3dDevCapResult result;
};
struct vmw_winsys_screen
{
@ -63,7 +70,10 @@ struct vmw_winsys_screen
struct {
int drm_fd;
uint32_t hwversion;
uint32_t *buffer;
uint32_t num_cap_3d;
struct vmw_cap_3d *cap_3d;
uint64_t max_mob_memory;
uint64_t max_surface_memory;
} ioctl;
struct {
@ -72,10 +82,21 @@ struct vmw_winsys_screen
struct pb_manager *gmr_fenced;
struct pb_manager *gmr_slab;
struct pb_manager *gmr_slab_fenced;
struct pb_manager *query;
struct pb_manager *query_mm;
struct pb_manager *query_fenced;
struct pb_manager *mob_fenced;
struct pb_manager *mob_cache;
struct pb_manager *mob_shader_slab;
struct pb_manager *mob_shader_slab_fenced;
} pools;
struct pb_fence_ops *fence_ops;
/*
* Screen instances
*/
dev_t device;
int open_count;
};
@ -86,6 +107,9 @@ vmw_winsys_screen(struct svga_winsys_screen *base)
}
/* */
uint32_t
vmw_region_size(struct vmw_region *region);
uint32
vmw_ioctl_context_create(struct vmw_winsys_screen *vws);
@ -100,6 +124,23 @@ vmw_ioctl_surface_create(struct vmw_winsys_screen *vws,
SVGA3dSize size,
uint32 numFaces,
uint32 numMipLevels);
uint32
vmw_ioctl_gb_surface_create(struct vmw_winsys_screen *vws,
SVGA3dSurfaceFlags flags,
SVGA3dSurfaceFormat format,
SVGA3dSize size,
uint32 numFaces,
uint32 numMipLevels,
uint32 buffer_handle,
struct vmw_region **p_region);
int
vmw_ioctl_gb_surface_ref(struct vmw_winsys_screen *vws,
uint32_t handle,
SVGA3dSurfaceFlags *flags,
SVGA3dSurfaceFormat *format,
uint32_t *numMipLevels,
struct vmw_region **p_region);
void
vmw_ioctl_surface_destroy(struct vmw_winsys_screen *vws,
@ -140,12 +181,28 @@ void
vmw_ioctl_fence_unref(struct vmw_winsys_screen *vws,
uint32_t handle);
uint32
vmw_ioctl_shader_create(struct vmw_winsys_screen *vws,
SVGA3dShaderType type,
uint32 code_len);
void
vmw_ioctl_shader_destroy(struct vmw_winsys_screen *vws, uint32 shid);
int
vmw_ioctl_syncforcpu(struct vmw_region *region,
boolean dont_block,
boolean readonly,
boolean allow_cs);
void
vmw_ioctl_releasefromcpu(struct vmw_region *region,
boolean readonly,
boolean allow_cs);
/* Initialize parts of vmw_winsys_screen at startup:
*/
boolean vmw_ioctl_init(struct vmw_winsys_screen *vws);
boolean vmw_pools_init(struct vmw_winsys_screen *vws);
boolean vmw_query_pools_init(struct vmw_winsys_screen *vws);
boolean vmw_mob_pools_init(struct vmw_winsys_screen *vws);
boolean vmw_winsys_screen_init_svga(struct vmw_winsys_screen *vws);
void vmw_ioctl_cleanup(struct vmw_winsys_screen *vws);
@ -156,4 +213,13 @@ void vmw_winsys_destroy(struct vmw_winsys_screen *sws);
void vmw_winsys_screen_set_throttling(struct pipe_screen *screen,
uint32_t throttle_us);
struct pb_manager *
simple_fenced_bufmgr_create(struct pb_manager *provider,
struct pb_fence_ops *ops);
void
vmw_fences_signal(struct pb_fence_ops *fence_ops,
uint32_t signaled,
uint32_t emitted,
boolean has_emitted);
#endif /* VMW_SCREEN_H_ */

View file

@ -32,7 +32,9 @@
#include "vmw_context.h"
#include "vmw_screen.h"
#include "vmw_surface.h"
#include "vmw_buffer.h"
#include "svga_drm_public.h"
#include "svga3d_surfacedefs.h"
#include "state_tracker/drm_driver.h"
@ -52,6 +54,11 @@ static struct svga_winsys_surface *
vmw_drm_surface_from_handle(struct svga_winsys_screen *sws,
struct winsys_handle *whandle,
SVGA3dSurfaceFormat *format);
static struct svga_winsys_surface *
vmw_drm_gb_surface_from_handle(struct svga_winsys_screen *sws,
struct winsys_handle *whandle,
SVGA3dSurfaceFormat *format);
static boolean
vmw_drm_surface_get_handle(struct svga_winsys_screen *sws,
struct svga_winsys_surface *surface,
@ -109,7 +116,8 @@ svga_drm_winsys_screen_create(int fd)
goto out_no_vws;
/* XXX do this properly */
vws->base.surface_from_handle = vmw_drm_surface_from_handle;
vws->base.surface_from_handle = vws->base.have_gb_objects ?
vmw_drm_gb_surface_from_handle : vmw_drm_surface_from_handle;
vws->base.surface_get_handle = vmw_drm_surface_get_handle;
return &vws->base;
@ -150,6 +158,83 @@ vmw_dri1_intersect_src_bbox(struct drm_clip_rect *dst,
return TRUE;
}
/**
* vmw_drm_gb_surface_from_handle - Create a shared surface
*
* @sws: Screen to register the surface with.
* @whandle: struct winsys_handle identifying the kernel surface object
* @format: On successful return points to a value describing the
* surface format.
*
* Returns a refcounted pointer to a struct svga_winsys_surface
* embedded in a struct vmw_svga_winsys_surface on success or NULL
* on failure.
*/
static struct svga_winsys_surface *
vmw_drm_gb_surface_from_handle(struct svga_winsys_screen *sws,
struct winsys_handle *whandle,
SVGA3dSurfaceFormat *format)
{
struct vmw_svga_winsys_surface *vsrf;
struct svga_winsys_surface *ssrf;
struct vmw_winsys_screen *vws = vmw_winsys_screen(sws);
SVGA3dSurfaceFlags flags;
uint32_t mip_levels;
struct vmw_buffer_desc desc;
struct pb_manager *provider = vws->pools.gmr;
struct pb_buffer *pb_buf;
int ret;
ret = vmw_ioctl_gb_surface_ref(vws, whandle->handle, &flags, format,
&mip_levels, &desc.region);
if (ret) {
fprintf(stderr, "Failed referencing shared surface. SID %d.\n"
"Error %d (%s).\n",
whandle->handle, ret, strerror(-ret));
return NULL;
}
if (mip_levels != 1) {
fprintf(stderr, "Incorrect number of mipmap levels on shared surface."
" SID %d, levels %d\n",
whandle->handle, mip_levels);
goto out_mip;
}
vsrf = CALLOC_STRUCT(vmw_svga_winsys_surface);
if (!vsrf)
goto out_mip;
pipe_reference_init(&vsrf->refcnt, 1);
p_atomic_set(&vsrf->validated, 0);
vsrf->screen = vws;
vsrf->sid = whandle->handle;
vsrf->size = vmw_region_size(desc.region);
/*
* Synchronize backing buffers of shared surfaces using the
* kernel, since we don't pass fence objects around between
* processes.
*/
desc.pb_desc.alignment = 4096;
desc.pb_desc.usage = VMW_BUFFER_USAGE_SHARED | VMW_BUFFER_USAGE_SYNC;
pb_buf = provider->create_buffer(provider, vsrf->size, &desc.pb_desc);
vsrf->buf = vmw_svga_winsys_buffer_wrap(pb_buf);
if (!vsrf->buf)
goto out_no_buf;
ssrf = svga_winsys_surface(vsrf);
return ssrf;
out_no_buf:
FREE(vsrf);
out_mip:
vmw_ioctl_region_destroy(desc.region);
vmw_ioctl_surface_destroy(vws, whandle->handle);
return NULL;
}
static struct svga_winsys_surface *
vmw_drm_surface_from_handle(struct svga_winsys_screen *sws,
struct winsys_handle *whandle,
@ -162,6 +247,7 @@ vmw_drm_surface_from_handle(struct svga_winsys_screen *sws,
struct drm_vmw_surface_arg *req = &arg.req;
struct drm_vmw_surface_create_req *rep = &arg.rep;
uint32_t handle = 0;
SVGA3dSize size;
int ret;
int i;
@ -187,6 +273,7 @@ vmw_drm_surface_from_handle(struct svga_winsys_screen *sws,
memset(&arg, 0, sizeof(arg));
req->sid = handle;
rep->size_addr = (size_t)&size;
ret = drmCommandWriteRead(vws->ioctl.drm_fd, DRM_VMW_REF_SURFACE,
&arg, sizeof(arg));
@ -235,6 +322,11 @@ vmw_drm_surface_from_handle(struct svga_winsys_screen *sws,
ssrf = svga_winsys_surface(vsrf);
*format = rep->format;
/* Estimate usage, for early flushing. */
vsrf->size = svga3dsurface_get_serialized_size(rep->format, size,
rep->mip_levels[0],
FALSE);
return ssrf;
out_mip:

View file

@ -43,6 +43,7 @@
#include "xf86drm.h"
#include "vmwgfx_drm.h"
#include "svga3d_caps.h"
#include "svga3d_reg.h"
#include "os/os_mman.h"
@ -51,7 +52,6 @@
struct vmw_region
{
SVGAGuestPtr ptr;
uint32_t handle;
uint64_t map_handle;
void *data;
@ -66,6 +66,13 @@ struct vmw_region
*/
#define SVGA3D_SURFACE_HINT_SCANOUT (1 << 9)
uint32_t
vmw_region_size(struct vmw_region *region)
{
return region->size;
}
uint32
vmw_ioctl_context_create(struct vmw_winsys_screen *vws)
{
@ -168,6 +175,139 @@ vmw_ioctl_surface_create(struct vmw_winsys_screen *vws,
return rep->sid;
}
uint32
vmw_ioctl_gb_surface_create(struct vmw_winsys_screen *vws,
SVGA3dSurfaceFlags flags,
SVGA3dSurfaceFormat format,
SVGA3dSize size,
uint32_t numFaces,
uint32_t numMipLevels,
uint32_t buffer_handle,
struct vmw_region **p_region)
{
union drm_vmw_gb_surface_create_arg s_arg;
struct drm_vmw_gb_surface_create_req *req = &s_arg.req;
struct drm_vmw_gb_surface_create_rep *rep = &s_arg.rep;
struct vmw_region *region = NULL;
int ret;
vmw_printf("%s flags %d format %d\n", __FUNCTION__, flags, format);
if (p_region) {
region = CALLOC_STRUCT(vmw_region);
if (!region)
return SVGA3D_INVALID_ID;
}
memset(&s_arg, 0, sizeof(s_arg));
if (flags & SVGA3D_SURFACE_HINT_SCANOUT) {
req->svga3d_flags = (uint32_t) (flags & ~SVGA3D_SURFACE_HINT_SCANOUT);
req->drm_surface_flags = drm_vmw_surface_flag_scanout;
} else {
req->svga3d_flags = (uint32_t) flags;
}
req->format = (uint32_t) format;
req->drm_surface_flags |= drm_vmw_surface_flag_shareable;
req->drm_surface_flags |= drm_vmw_surface_flag_create_buffer;
assert(numFaces * numMipLevels < DRM_VMW_MAX_SURFACE_FACES*
DRM_VMW_MAX_MIP_LEVELS);
req->base_size.width = size.width;
req->base_size.height = size.height;
req->base_size.depth = size.depth;
req->mip_levels = numMipLevels;
req->multisample_count = 0;
req->autogen_filter = SVGA3D_TEX_FILTER_NONE;
if (buffer_handle)
req->buffer_handle = buffer_handle;
else
req->buffer_handle = SVGA3D_INVALID_ID;
ret = drmCommandWriteRead(vws->ioctl.drm_fd, DRM_VMW_GB_SURFACE_CREATE,
&s_arg, sizeof(s_arg));
if (ret)
goto out_fail_create;
if (p_region) {
region->handle = rep->buffer_handle;
region->map_handle = rep->buffer_map_handle;
region->drm_fd = vws->ioctl.drm_fd;
region->size = rep->backup_size;
*p_region = region;
}
vmw_printf("Surface id is %d\n", rep->sid);
return rep->handle;
out_fail_create:
if (region)
FREE(region);
return SVGA3D_INVALID_ID;
}
/**
* vmw_ioctl_gb_surface_ref - Put a reference on a guest-backed surface and
* get surface information
*
* @vws: Screen to register the reference on
* @handle: Kernel handle of the guest-backed surface
* @flags: flags used when the surface was created
* @format: Format used when the surface was created
* @numMipLevels: Number of mipmap levels of the surface
* @p_region: On successful return points to a newly allocated
* struct vmw_region holding a reference to the surface backup buffer.
*
* Returns 0 on success, a system error on failure.
*/
int
vmw_ioctl_gb_surface_ref(struct vmw_winsys_screen *vws,
uint32_t handle,
SVGA3dSurfaceFlags *flags,
SVGA3dSurfaceFormat *format,
uint32_t *numMipLevels,
struct vmw_region **p_region)
{
union drm_vmw_gb_surface_reference_arg s_arg;
struct drm_vmw_surface_arg *req = &s_arg.req;
struct drm_vmw_gb_surface_ref_rep *rep = &s_arg.rep;
struct vmw_region *region = NULL;
int ret;
vmw_printf("%s flags %d format %d\n", __FUNCTION__, flags, format);
assert(p_region != NULL);
region = CALLOC_STRUCT(vmw_region);
if (!region)
return -ENOMEM;
memset(&s_arg, 0, sizeof(s_arg));
req->sid = handle;
ret = drmCommandWriteRead(vws->ioctl.drm_fd, DRM_VMW_GB_SURFACE_REF,
&s_arg, sizeof(s_arg));
if (ret)
goto out_fail_ref;
region->handle = rep->crep.buffer_handle;
region->map_handle = rep->crep.buffer_map_handle;
region->drm_fd = vws->ioctl.drm_fd;
region->size = rep->crep.backup_size;
*p_region = region;
*flags = rep->creq.svga3d_flags;
*format = rep->creq.format;
*numMipLevels = rep->creq.mip_levels;
return 0;
out_fail_ref:
if (region)
FREE(region);
return ret;
}
void
vmw_ioctl_surface_destroy(struct vmw_winsys_screen *vws, uint32 sid)
{
@ -238,8 +378,11 @@ vmw_ioctl_command(struct vmw_winsys_screen *vws, int32_t cid,
*pfence = NULL;
} else {
if (pfence) {
*pfence = vmw_fence_create(rep.handle, rep.mask);
vmw_fences_signal(vws->fence_ops, rep.passed_seqno, rep.seqno,
TRUE);
*pfence = vmw_fence_create(vws->fence_ops, rep.handle,
rep.seqno, rep.mask);
if (*pfence == NULL) {
/*
* Fence creation failed. Need to sync.
@ -279,8 +422,6 @@ vmw_ioctl_region_create(struct vmw_winsys_screen *vws, uint32_t size)
goto out_err1;
}
region->ptr.gmrId = rep->cur_gmr_id;
region->ptr.offset = rep->cur_gmr_offset;
region->data = NULL;
region->handle = rep->handle;
region->map_handle = rep->map_handle;
@ -321,7 +462,8 @@ vmw_ioctl_region_destroy(struct vmw_region *region)
SVGAGuestPtr
vmw_ioctl_region_ptr(struct vmw_region *region)
{
return region->ptr;
SVGAGuestPtr ptr = {region->handle, 0};
return ptr;
}
void *
@ -356,6 +498,69 @@ vmw_ioctl_region_unmap(struct vmw_region *region)
--region->map_count;
}
/**
* vmw_ioctl_syncforcpu - Synchronize a buffer object for CPU usage
*
* @region: Pointer to a struct vmw_region representing the buffer object.
* @dont_block: Dont wait for GPU idle, but rather return -EBUSY if the
* GPU is busy with the buffer object.
* @readonly: Hint that the CPU access is read-only.
* @allow_cs: Allow concurrent command submission while the buffer is
* synchronized for CPU. If FALSE command submissions referencing the
* buffer will block until a corresponding call to vmw_ioctl_releasefromcpu.
*
* This function idles any GPU activities touching the buffer and blocks
* command submission of commands referencing the buffer, even from
* other processes.
*/
int
vmw_ioctl_syncforcpu(struct vmw_region *region,
boolean dont_block,
boolean readonly,
boolean allow_cs)
{
struct drm_vmw_synccpu_arg arg;
memset(&arg, 0, sizeof(arg));
arg.op = drm_vmw_synccpu_grab;
arg.handle = region->handle;
arg.flags = drm_vmw_synccpu_read;
if (!readonly)
arg.flags |= drm_vmw_synccpu_write;
if (dont_block)
arg.flags |= drm_vmw_synccpu_dontblock;
if (allow_cs)
arg.flags |= drm_vmw_synccpu_allow_cs;
return drmCommandWrite(region->drm_fd, DRM_VMW_SYNCCPU, &arg, sizeof(arg));
}
/**
* vmw_ioctl_releasefromcpu - Undo a previous syncforcpu.
*
* @region: Pointer to a struct vmw_region representing the buffer object.
* @readonly: Should hold the same value as the matching syncforcpu call.
* @allow_cs: Should hold the same value as the matching syncforcpu call.
*/
void
vmw_ioctl_releasefromcpu(struct vmw_region *region,
boolean readonly,
boolean allow_cs)
{
struct drm_vmw_synccpu_arg arg;
memset(&arg, 0, sizeof(arg));
arg.op = drm_vmw_synccpu_release;
arg.handle = region->handle;
arg.flags = drm_vmw_synccpu_read;
if (!readonly)
arg.flags |= drm_vmw_synccpu_write;
if (allow_cs)
arg.flags |= drm_vmw_synccpu_allow_cs;
(void) drmCommandWrite(region->drm_fd, DRM_VMW_SYNCCPU, &arg, sizeof(arg));
}
void
vmw_ioctl_fence_unref(struct vmw_winsys_screen *vws,
uint32_t handle)
@ -405,6 +610,8 @@ vmw_ioctl_fence_signalled(struct vmw_winsys_screen *vws,
if (ret != 0)
return ret;
vmw_fences_signal(vws->fence_ops, arg.passed_seqno, 0, FALSE);
return (arg.signaled) ? 0 : -1;
}
@ -435,6 +642,113 @@ vmw_ioctl_fence_finish(struct vmw_winsys_screen *vws,
return 0;
}
uint32
vmw_ioctl_shader_create(struct vmw_winsys_screen *vws,
SVGA3dShaderType type,
uint32 code_len)
{
struct drm_vmw_shader_create_arg sh_arg;
int ret;
VMW_FUNC;
memset(&sh_arg, 0, sizeof(sh_arg));
sh_arg.size = code_len;
sh_arg.buffer_handle = SVGA3D_INVALID_ID;
sh_arg.shader_handle = SVGA3D_INVALID_ID;
switch (type) {
case SVGA3D_SHADERTYPE_VS:
sh_arg.shader_type = drm_vmw_shader_type_vs;
break;
case SVGA3D_SHADERTYPE_PS:
sh_arg.shader_type = drm_vmw_shader_type_ps;
break;
default:
assert(!"Invalid shader type.");
break;
}
ret = drmCommandWriteRead(vws->ioctl.drm_fd, DRM_VMW_CREATE_SHADER,
&sh_arg, sizeof(sh_arg));
if (ret)
return SVGA3D_INVALID_ID;
return sh_arg.shader_handle;
}
void
vmw_ioctl_shader_destroy(struct vmw_winsys_screen *vws, uint32 shid)
{
struct drm_vmw_shader_arg sh_arg;
VMW_FUNC;
memset(&sh_arg, 0, sizeof(sh_arg));
sh_arg.handle = shid;
(void)drmCommandWrite(vws->ioctl.drm_fd, DRM_VMW_UNREF_SHADER,
&sh_arg, sizeof(sh_arg));
}
static int
vmw_ioctl_parse_caps(struct vmw_winsys_screen *vws,
const uint32_t *cap_buffer)
{
int i;
if (vws->base.have_gb_objects) {
for (i = 0; i < vws->ioctl.num_cap_3d; ++i) {
vws->ioctl.cap_3d[i].has_cap = TRUE;
vws->ioctl.cap_3d[i].result.u = cap_buffer[i];
}
return 0;
} else {
const uint32 *capsBlock;
const SVGA3dCapsRecord *capsRecord = NULL;
uint32 offset;
const SVGA3dCapPair *capArray;
int numCaps, index;
/*
* Search linearly through the caps block records for the specified type.
*/
capsBlock = cap_buffer;
for (offset = 0; capsBlock[offset] != 0; offset += capsBlock[offset]) {
const SVGA3dCapsRecord *record;
assert(offset < SVGA_FIFO_3D_CAPS_SIZE);
record = (const SVGA3dCapsRecord *) (capsBlock + offset);
if ((record->header.type >= SVGA3DCAPS_RECORD_DEVCAPS_MIN) &&
(record->header.type <= SVGA3DCAPS_RECORD_DEVCAPS_MAX) &&
(!capsRecord || (record->header.type > capsRecord->header.type))) {
capsRecord = record;
}
}
if(!capsRecord)
return -1;
/*
* Calculate the number of caps from the size of the record.
*/
capArray = (const SVGA3dCapPair *) capsRecord->data;
numCaps = (int) ((capsRecord->header.length * sizeof(uint32) -
sizeof capsRecord->header) / (2 * sizeof(uint32)));
for (i = 0; i < numCaps; i++) {
index = capArray[i][0];
if (index < vws->ioctl.num_cap_3d) {
vws->ioctl.cap_3d[index].has_cap = TRUE;
vws->ioctl.cap_3d[index].result.u = capArray[i][1];
} else {
debug_printf("Unknown devcaps seen: %d\n", index);
}
}
}
return 0;
}
boolean
vmw_ioctl_init(struct vmw_winsys_screen *vws)
@ -443,9 +757,19 @@ vmw_ioctl_init(struct vmw_winsys_screen *vws)
struct drm_vmw_get_3d_cap_arg cap_arg;
unsigned int size;
int ret;
uint32_t *cap_buffer;
drmVersionPtr version;
boolean drm_gb_capable;
VMW_FUNC;
version = drmGetVersion(vws->ioctl.drm_fd);
if (!version)
goto out_no_version;
drm_gb_capable = version->version_major > 2 ||
(version->version_major == 2 && version->version_minor > 4);
memset(&gp_arg, 0, sizeof(gp_arg));
gp_arg.param = DRM_VMW_PARAM_3D;
ret = drmCommandWriteRead(vws->ioctl.drm_fd, DRM_VMW_GET_PARAM,
@ -466,15 +790,78 @@ vmw_ioctl_init(struct vmw_winsys_screen *vws)
}
vws->ioctl.hwversion = gp_arg.value;
size = SVGA_FIFO_3D_CAPS_SIZE * sizeof(uint32_t);
vws->ioctl.buffer = calloc(1, size);
if (!vws->ioctl.buffer) {
memset(&gp_arg, 0, sizeof(gp_arg));
gp_arg.param = DRM_VMW_PARAM_HW_CAPS;
ret = drmCommandWriteRead(vws->ioctl.drm_fd, DRM_VMW_GET_PARAM,
&gp_arg, sizeof(gp_arg));
if (ret)
vws->base.have_gb_objects = FALSE;
else
vws->base.have_gb_objects =
!!(gp_arg.value & (uint64_t) SVGA_CAP_GBOBJECTS);
if (vws->base.have_gb_objects && !drm_gb_capable)
goto out_no_3d;
if (vws->base.have_gb_objects) {
memset(&gp_arg, 0, sizeof(gp_arg));
gp_arg.param = DRM_VMW_PARAM_3D_CAPS_SIZE;
ret = drmCommandWriteRead(vws->ioctl.drm_fd, DRM_VMW_GET_PARAM,
&gp_arg, sizeof(gp_arg));
if (ret)
size = SVGA_FIFO_3D_CAPS_SIZE * sizeof(uint32_t);
else
size = gp_arg.value;
if (vws->base.have_gb_objects)
vws->ioctl.num_cap_3d = size / sizeof(uint32_t);
else
vws->ioctl.num_cap_3d = SVGA3D_DEVCAP_MAX;
memset(&gp_arg, 0, sizeof(gp_arg));
gp_arg.param = DRM_VMW_PARAM_MAX_MOB_MEMORY;
ret = drmCommandWriteRead(vws->ioctl.drm_fd, DRM_VMW_GET_PARAM,
&gp_arg, sizeof(gp_arg));
if (ret) {
/* Just guess a large enough value. */
vws->ioctl.max_mob_memory = 256*1024*1024;
} else {
vws->ioctl.max_mob_memory = gp_arg.value;
}
/* Never early flush surfaces, mobs do accounting. */
vws->ioctl.max_surface_memory = -1;
} else {
vws->ioctl.num_cap_3d = SVGA3D_DEVCAP_MAX;
memset(&gp_arg, 0, sizeof(gp_arg));
gp_arg.param = DRM_VMW_PARAM_MAX_SURF_MEMORY;
ret = drmCommandWriteRead(vws->ioctl.drm_fd, DRM_VMW_GET_PARAM,
&gp_arg, sizeof(gp_arg));
if (ret) {
/* Just guess a large enough value, around 800mb. */
vws->ioctl.max_surface_memory = 0x300000000;
} else {
vws->ioctl.max_surface_memory = gp_arg.value;
}
size = SVGA_FIFO_3D_CAPS_SIZE * sizeof(uint32_t);
}
cap_buffer = calloc(1, size);
if (!cap_buffer) {
debug_printf("Failed alloc fifo 3D caps buffer.\n");
goto out_no_3d;
}
vws->ioctl.cap_3d = calloc(vws->ioctl.num_cap_3d,
sizeof(*vws->ioctl.cap_3d));
if (!vws->ioctl.cap_3d) {
debug_printf("Failed alloc fifo 3D caps buffer.\n");
goto out_no_caparray;
}
memset(&cap_arg, 0, sizeof(cap_arg));
cap_arg.buffer = (uint64_t) (unsigned long) (vws->ioctl.buffer);
cap_arg.buffer = (uint64_t) (unsigned long) (cap_buffer);
cap_arg.max_size = size;
ret = drmCommandWrite(vws->ioctl.drm_fd, DRM_VMW_GET_3D_CAP,
@ -486,11 +873,24 @@ vmw_ioctl_init(struct vmw_winsys_screen *vws)
goto out_no_caps;
}
ret = vmw_ioctl_parse_caps(vws, cap_buffer);
if (ret) {
debug_printf("Failed to parse 3D capabilities"
" (%i, %s).\n", ret, strerror(-ret));
goto out_no_caps;
}
free(cap_buffer);
drmFreeVersion(version);
vmw_printf("%s OK\n", __FUNCTION__);
return TRUE;
out_no_caps:
free(vws->ioctl.buffer);
free(vws->ioctl.cap_3d);
out_no_caparray:
free(cap_buffer);
out_no_3d:
drmFreeVersion(version);
out_no_version:
vws->ioctl.num_cap_3d = 0;
debug_printf("%s Failed\n", __FUNCTION__);
return FALSE;
}

View file

@ -32,13 +32,6 @@
#include "pipebuffer/pb_buffer.h"
#include "pipebuffer/pb_bufmgr.h"
/*
* TODO: Have the query pool always ask the fence manager for
* SVGA_FENCE_FLAG_QUERY signaled. Unfortunately, pb_fenced doesn't
* support that currently, so we'd have to create a separate
* pb_fence_ops wrapper that does this implicitly.
*/
/**
* vmw_pools_cleanup - Destroy the buffer pools.
*
@ -47,20 +40,32 @@
void
vmw_pools_cleanup(struct vmw_winsys_screen *vws)
{
if(vws->pools.gmr_fenced)
vws->pools.gmr_fenced->destroy(vws->pools.gmr_fenced);
if (vws->pools.mob_shader_slab_fenced)
vws->pools.mob_shader_slab_fenced->destroy
(vws->pools.mob_shader_slab_fenced);
if (vws->pools.mob_shader_slab)
vws->pools.mob_shader_slab->destroy(vws->pools.mob_shader_slab);
if (vws->pools.mob_fenced)
vws->pools.mob_fenced->destroy(vws->pools.mob_fenced);
if (vws->pools.mob_cache)
vws->pools.mob_cache->destroy(vws->pools.mob_cache);
if (vws->pools.query_fenced)
vws->pools.query_fenced->destroy(vws->pools.query_fenced);
if (vws->pools.query_mm)
vws->pools.query_mm->destroy(vws->pools.query_mm);
/* gmr_mm pool is already destroyed above */
if(vws->pools.gmr_fenced)
vws->pools.gmr_fenced->destroy(vws->pools.gmr_fenced);
if (vws->pools.gmr_mm)
vws->pools.gmr_mm->destroy(vws->pools.gmr_mm);
if (vws->pools.gmr_slab_fenced)
vws->pools.gmr_slab_fenced->destroy(vws->pools.gmr_slab_fenced);
if (vws->pools.gmr_slab)
vws->pools.gmr_slab->destroy(vws->pools.gmr_slab);
if(vws->pools.gmr)
vws->pools.gmr->destroy(vws->pools.gmr);
if(vws->pools.query)
vws->pools.query->destroy(vws->pools.query);
}
@ -77,21 +82,14 @@ vmw_pools_cleanup(struct vmw_winsys_screen *vws)
boolean
vmw_query_pools_init(struct vmw_winsys_screen *vws)
{
vws->pools.query = vmw_gmr_bufmgr_create(vws);
if(!vws->pools.query)
return FALSE;
vws->pools.query_mm = mm_bufmgr_create(vws->pools.query,
vws->pools.query_mm = mm_bufmgr_create(vws->pools.gmr,
VMW_QUERY_POOL_SIZE,
3 /* 8 alignment */);
if(!vws->pools.query_mm)
goto out_no_query_mm;
if (!vws->pools.query_mm)
return FALSE;
vws->pools.query_fenced = fenced_bufmgr_create(
vws->pools.query_mm,
vmw_fence_ops_create(vws),
VMW_QUERY_POOL_SIZE,
~0);
vws->pools.query_fenced = simple_fenced_bufmgr_create(
vws->pools.query_mm, vws->fence_ops);
if(!vws->pools.query_fenced)
goto out_no_query_fenced;
@ -100,8 +98,60 @@ vmw_query_pools_init(struct vmw_winsys_screen *vws)
out_no_query_fenced:
vws->pools.query_mm->destroy(vws->pools.query_mm);
out_no_query_mm:
vws->pools.query->destroy(vws->pools.query);
return FALSE;
}
/**
* vmw_mob_pool_init - Create a pool of fenced kernel buffers.
*
* @vws: Pointer to a struct vmw_winsys_screen.
*
* Typically this pool should be created on demand when we
* detect that the app will be using MOB buffers.
*/
boolean
vmw_mob_pools_init(struct vmw_winsys_screen *vws)
{
struct pb_desc desc;
vws->pools.mob_cache =
pb_cache_manager_create(vws->pools.gmr, 100000, 2,
VMW_BUFFER_USAGE_SHARED);
if (!vws->pools.mob_cache)
return FALSE;
vws->pools.mob_fenced =
simple_fenced_bufmgr_create(vws->pools.mob_cache,
vws->fence_ops);
if(!vws->pools.mob_fenced)
goto out_no_mob_fenced;
desc.alignment = 64;
desc.usage = ~(SVGA_BUFFER_USAGE_PINNED | VMW_BUFFER_USAGE_SHARED |
VMW_BUFFER_USAGE_SYNC);
vws->pools.mob_shader_slab =
pb_slab_range_manager_create(vws->pools.mob_cache,
64,
8192,
16384,
&desc);
if(!vws->pools.mob_shader_slab)
goto out_no_mob_shader_slab;
vws->pools.mob_shader_slab_fenced =
simple_fenced_bufmgr_create(vws->pools.mob_shader_slab,
vws->fence_ops);
if(!vws->pools.mob_fenced)
goto out_no_mob_shader_slab_fenced;
return TRUE;
out_no_mob_shader_slab_fenced:
vws->pools.mob_shader_slab->destroy(vws->pools.mob_shader_slab);
out_no_mob_shader_slab:
vws->pools.mob_fenced->destroy(vws->pools.mob_fenced);
out_no_mob_fenced:
vws->pools.mob_cache->destroy(vws->pools.mob_cache);
return FALSE;
}
@ -119,33 +169,27 @@ vmw_pools_init(struct vmw_winsys_screen *vws)
if(!vws->pools.gmr)
goto error;
vws->pools.gmr_mm = mm_bufmgr_create(vws->pools.gmr,
VMW_GMR_POOL_SIZE,
12 /* 4096 alignment */);
if(!vws->pools.gmr_mm)
goto error;
if ((vws->base.have_gb_objects && vws->base.have_gb_dma) ||
!vws->base.have_gb_objects) {
/*
* A managed pool for DMA buffers.
*/
vws->pools.gmr_mm = mm_bufmgr_create(vws->pools.gmr,
VMW_GMR_POOL_SIZE,
12 /* 4096 alignment */);
if(!vws->pools.gmr_mm)
goto error;
/*
* We disallow "CPU" buffers to be created by the fenced_bufmgr_create,
* because that defers "GPU" buffer creation to buffer validation,
* and at buffer validation we have no means of handling failures
* due to pools space shortage or fragmentation. Effectively this
* makes sure all failures are reported immediately on buffer allocation,
* and we can revert to allocating directly from the kernel.
*/
vws->pools.gmr_fenced = fenced_bufmgr_create(
vws->pools.gmr_mm,
vmw_fence_ops_create(vws),
VMW_GMR_POOL_SIZE,
0);
vws->pools.gmr_fenced = simple_fenced_bufmgr_create
(vws->pools.gmr_mm, vws->fence_ops);
#ifdef DEBUG
vws->pools.gmr_fenced = pb_debug_manager_create(vws->pools.gmr_fenced,
4096,
4096);
vws->pools.gmr_fenced = pb_debug_manager_create(vws->pools.gmr_fenced,
4096,
4096);
#endif
if(!vws->pools.gmr_fenced)
goto error;
if(!vws->pools.gmr_fenced)
goto error;
/*
* The slab pool allocates buffers directly from the kernel except
@ -153,30 +197,33 @@ vmw_pools_init(struct vmw_winsys_screen *vws)
* not to waste memory, since a kernel buffer is a minimum 4096 bytes.
*
* Here we use it only for emergency in the case our pre-allocated
* buffer pool runs out of memory.
* managed buffer pool runs out of memory.
*/
desc.alignment = 64;
desc.usage = ~0;
vws->pools.gmr_slab = pb_slab_range_manager_create(vws->pools.gmr,
64,
8192,
16384,
&desc);
if (!vws->pools.gmr_slab)
goto error;
vws->pools.gmr_slab_fenced =
fenced_bufmgr_create(vws->pools.gmr_slab,
vmw_fence_ops_create(vws),
VMW_MAX_BUFFER_SIZE,
0);
desc.alignment = 64;
desc.usage = ~(SVGA_BUFFER_USAGE_PINNED | SVGA_BUFFER_USAGE_SHADER |
VMW_BUFFER_USAGE_SHARED | VMW_BUFFER_USAGE_SYNC);
vws->pools.gmr_slab = pb_slab_range_manager_create(vws->pools.gmr,
64,
8192,
16384,
&desc);
if (!vws->pools.gmr_slab)
goto error;
if (!vws->pools.gmr_slab_fenced)
goto error;
vws->pools.gmr_slab_fenced =
simple_fenced_bufmgr_create(vws->pools.gmr_slab, vws->fence_ops);
if (!vws->pools.gmr_slab_fenced)
goto error;
}
vws->pools.query_fenced = NULL;
vws->pools.query_mm = NULL;
vws->pools.query = NULL;
vws->pools.mob_cache = NULL;
if (vws->base.have_gb_objects && !vmw_mob_pools_init(vws))
goto error;
return TRUE;
@ -184,4 +231,3 @@ error:
vmw_pools_cleanup(vws);
return FALSE;
}

View file

@ -47,7 +47,14 @@
#include "vmw_surface.h"
#include "vmw_buffer.h"
#include "vmw_fence.h"
#include "vmw_shader.h"
#include "svga3d_surfacedefs.h"
/**
* Try to get a surface backing buffer from the cache
* if it's this size or smaller.
*/
#define VMW_TRY_CACHED_SIZE (2*1024*1024)
static struct svga_winsys_buffer *
vmw_svga_winsys_buffer_create(struct svga_winsys_screen *sws,
@ -56,64 +63,37 @@ vmw_svga_winsys_buffer_create(struct svga_winsys_screen *sws,
unsigned size)
{
struct vmw_winsys_screen *vws = vmw_winsys_screen(sws);
struct pb_desc desc;
struct vmw_buffer_desc desc;
struct pb_manager *provider;
struct pb_buffer *buffer;
memset(&desc, 0, sizeof desc);
desc.alignment = alignment;
desc.usage = usage;
desc.pb_desc.alignment = alignment;
desc.pb_desc.usage = usage;
if (usage == SVGA_BUFFER_USAGE_PINNED) {
if (vws->pools.query_fenced == NULL && !vmw_query_pools_init(vws))
return NULL;
provider = vws->pools.query_fenced;
} else if (usage == SVGA_BUFFER_USAGE_SHADER) {
provider = vws->pools.mob_shader_slab_fenced;
} else
provider = vws->pools.gmr_fenced;
assert(provider);
buffer = provider->create_buffer(provider, size, &desc);
buffer = provider->create_buffer(provider, size, &desc.pb_desc);
if(!buffer && provider == vws->pools.gmr_fenced) {
assert(provider);
provider = vws->pools.gmr_slab_fenced;
buffer = provider->create_buffer(provider, size, &desc);
buffer = provider->create_buffer(provider, size, &desc.pb_desc);
}
if (!buffer)
return NULL;
return vmw_svga_winsys_buffer(buffer);
}
static void *
vmw_svga_winsys_buffer_map(struct svga_winsys_screen *sws,
struct svga_winsys_buffer *buf,
unsigned flags)
{
(void)sws;
return pb_map(vmw_pb_buffer(buf), flags, NULL);
}
static void
vmw_svga_winsys_buffer_unmap(struct svga_winsys_screen *sws,
struct svga_winsys_buffer *buf)
{
(void)sws;
pb_unmap(vmw_pb_buffer(buf));
}
static void
vmw_svga_winsys_buffer_destroy(struct svga_winsys_screen *sws,
struct svga_winsys_buffer *buf)
{
struct pb_buffer *pbuf = vmw_pb_buffer(buf);
(void)sws;
pb_reference(&pbuf, NULL);
return vmw_svga_winsys_buffer_wrap(buffer);
}
@ -161,7 +141,12 @@ vmw_svga_winsys_surface_create(struct svga_winsys_screen *sws,
{
struct vmw_winsys_screen *vws = vmw_winsys_screen(sws);
struct vmw_svga_winsys_surface *surface;
struct vmw_buffer_desc desc;
struct pb_manager *provider = vws->pools.mob_fenced;
uint32_t buffer_size;
memset(&desc, 0, sizeof(desc));
surface = CALLOC_STRUCT(vmw_svga_winsys_surface);
if(!surface)
goto no_surface;
@ -169,15 +154,96 @@ vmw_svga_winsys_surface_create(struct svga_winsys_screen *sws,
pipe_reference_init(&surface->refcnt, 1);
p_atomic_set(&surface->validated, 0);
surface->screen = vws;
surface->sid = vmw_ioctl_surface_create(vws,
flags, format, size,
numFaces, numMipLevels);
if(surface->sid == SVGA3D_INVALID_ID)
goto no_sid;
pipe_mutex_init(surface->mutex);
/*
* Used for the backing buffer GB surfaces, and to approximate
* when to flush on non-GB hosts.
*/
buffer_size = svga3dsurface_get_serialized_size(format, size, numMipLevels, (numFaces == 6));
if (sws->have_gb_objects) {
SVGAGuestPtr ptr = {0,0};
/*
* If the backing buffer size is small enough, try to allocate a
* buffer out of the buffer cache. Otherwise, let the kernel allocate
* a suitable buffer for us.
*/
if (buffer_size < VMW_TRY_CACHED_SIZE) {
struct pb_buffer *pb_buf;
surface->size = buffer_size;
desc.pb_desc.alignment = 4096;
desc.pb_desc.usage = 0;
pb_buf = provider->create_buffer(provider, buffer_size, &desc.pb_desc);
surface->buf = vmw_svga_winsys_buffer_wrap(pb_buf);
if (surface->buf && !vmw_gmr_bufmgr_region_ptr(pb_buf, &ptr))
assert(0);
}
surface->sid = vmw_ioctl_gb_surface_create(vws,
flags, format, size,
numFaces, numMipLevels,
ptr.gmrId,
surface->buf ? NULL :
&desc.region);
if (surface->sid == SVGA3D_INVALID_ID && surface->buf) {
/*
* Kernel refused to allocate a surface for us.
* Perhaps something was wrong with our buffer?
* This is really a guard against future new size requirements
* on the backing buffers.
*/
vmw_svga_winsys_buffer_destroy(sws, surface->buf);
surface->buf = NULL;
surface->sid = vmw_ioctl_gb_surface_create(vws,
flags, format, size,
numFaces, numMipLevels,
0,
&desc.region);
if (surface->sid == SVGA3D_INVALID_ID)
goto no_sid;
}
/*
* If the kernel created the buffer for us, wrap it into a
* vmw_svga_winsys_buffer.
*/
if (surface->buf == NULL) {
struct pb_buffer *pb_buf;
surface->size = vmw_region_size(desc.region);
desc.pb_desc.alignment = 4096;
desc.pb_desc.usage = VMW_BUFFER_USAGE_SHARED;
pb_buf = provider->create_buffer(provider, surface->size,
&desc.pb_desc);
surface->buf = vmw_svga_winsys_buffer_wrap(pb_buf);
if (surface->buf == NULL) {
vmw_ioctl_region_destroy(desc.region);
vmw_ioctl_surface_destroy(vws, surface->sid);
goto no_sid;
}
}
} else {
surface->sid = vmw_ioctl_surface_create(vws,
flags, format, size,
numFaces, numMipLevels);
if(surface->sid == SVGA3D_INVALID_ID)
goto no_sid;
/* Best estimate for surface size, used for early flushing. */
surface->size = buffer_size;
surface->buf = NULL;
}
return svga_winsys_surface(surface);
no_sid:
if (surface->buf)
vmw_svga_winsys_buffer_destroy(sws, surface->buf);
FREE(surface);
no_surface:
return NULL;
@ -220,6 +286,9 @@ vmw_svga_winsys_get_hw_version(struct svga_winsys_screen *sws)
{
struct vmw_winsys_screen *vws = vmw_winsys_screen(sws);
if (sws->have_gb_objects)
return SVGA3D_HWVERSION_WS8_B1;
return (SVGA3dHardwareVersion) vws->ioctl.hwversion;
}
@ -228,69 +297,69 @@ static boolean
vmw_svga_winsys_get_cap(struct svga_winsys_screen *sws,
SVGA3dDevCapIndex index,
SVGA3dDevCapResult *result)
{
{
struct vmw_winsys_screen *vws = vmw_winsys_screen(sws);
const uint32 *capsBlock;
const SVGA3dCapsRecord *capsRecord = NULL;
uint32 offset;
const SVGA3dCapPair *capArray;
int numCaps, first, last;
if(vws->ioctl.hwversion < SVGA3D_HWVERSION_WS6_B1)
if (index > vws->ioctl.num_cap_3d || !vws->ioctl.cap_3d[index].has_cap)
return FALSE;
/*
* Search linearly through the caps block records for the specified type.
*/
capsBlock = (const uint32 *)vws->ioctl.buffer;
for (offset = 0; capsBlock[offset] != 0; offset += capsBlock[offset]) {
const SVGA3dCapsRecord *record;
assert(offset < SVGA_FIFO_3D_CAPS_SIZE);
record = (const SVGA3dCapsRecord *) (capsBlock + offset);
if ((record->header.type >= SVGA3DCAPS_RECORD_DEVCAPS_MIN) &&
(record->header.type <= SVGA3DCAPS_RECORD_DEVCAPS_MAX) &&
(!capsRecord || (record->header.type > capsRecord->header.type))) {
capsRecord = record;
}
}
if(!capsRecord)
return FALSE;
/*
* Calculate the number of caps from the size of the record.
*/
capArray = (const SVGA3dCapPair *) capsRecord->data;
numCaps = (int) ((capsRecord->header.length * sizeof(uint32) -
sizeof capsRecord->header) / (2 * sizeof(uint32)));
/*
* Binary-search for the cap with the specified index.
*/
for (first = 0, last = numCaps - 1; first <= last; ) {
int mid = (first + last) / 2;
if ((SVGA3dDevCapIndex) capArray[mid][0] == index) {
/*
* Found it.
*/
result->u = capArray[mid][1];
return TRUE;
}
/*
* Divide and conquer.
*/
if ((SVGA3dDevCapIndex) capArray[mid][0] > index) {
last = mid - 1;
} else {
first = mid + 1;
}
}
return FALSE;
*result = vws->ioctl.cap_3d[index].result;
return TRUE;
}
static struct svga_winsys_gb_shader *
vmw_svga_winsys_shader_create(struct svga_winsys_screen *sws,
SVGA3dShaderType type,
const uint32 *bytecode,
uint32 bytecodeLen)
{
struct vmw_winsys_screen *vws = vmw_winsys_screen(sws);
struct vmw_svga_winsys_shader *shader;
void *code;
shader = CALLOC_STRUCT(vmw_svga_winsys_shader);
if(!shader)
goto out_no_shader;
pipe_reference_init(&shader->refcnt, 1);
p_atomic_set(&shader->validated, 0);
shader->screen = vws;
shader->buf = vmw_svga_winsys_buffer_create(sws, 64,
SVGA_BUFFER_USAGE_SHADER,
bytecodeLen);
if (!shader->buf)
goto out_no_buf;
code = vmw_svga_winsys_buffer_map(sws, shader->buf, PIPE_TRANSFER_WRITE);
if (!code)
goto out_no_buf;
memcpy(code, bytecode, bytecodeLen);
vmw_svga_winsys_buffer_unmap(sws, shader->buf);
shader->shid = vmw_ioctl_shader_create(vws, type, bytecodeLen);
if(shader->shid == SVGA3D_INVALID_ID)
goto out_no_shid;
return svga_winsys_shader(shader);
out_no_shid:
vmw_svga_winsys_buffer_destroy(sws, shader->buf);
out_no_buf:
FREE(shader);
out_no_shader:
return NULL;
}
static void
vmw_svga_winsys_shader_destroy(struct svga_winsys_screen *sws,
struct svga_winsys_gb_shader *shader)
{
struct vmw_svga_winsys_shader *d_shader =
vmw_svga_winsys_shader(shader);
vmw_svga_winsys_shader_reference(&d_shader, NULL);
}
boolean
vmw_winsys_screen_init_svga(struct vmw_winsys_screen *vws)
@ -308,6 +377,8 @@ vmw_winsys_screen_init_svga(struct vmw_winsys_screen *vws)
vws->base.buffer_destroy = vmw_svga_winsys_buffer_destroy;
vws->base.fence_reference = vmw_svga_winsys_fence_reference;
vws->base.fence_signalled = vmw_svga_winsys_fence_signalled;
vws->base.shader_create = vmw_svga_winsys_shader_create;
vws->base.shader_destroy = vmw_svga_winsys_shader_destroy;
vws->base.fence_finish = vmw_svga_winsys_fence_finish;
return TRUE;

View file

@ -0,0 +1,64 @@
/**********************************************************
* Copyright 2009-2012 VMware, Inc. All rights reserved.
*
* 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 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 "svga_cmd.h"
#include "util/u_debug.h"
#include "util/u_memory.h"
#include "vmw_shader.h"
#include "vmw_screen.h"
void
vmw_svga_winsys_shader_reference(struct vmw_svga_winsys_shader **pdst,
struct vmw_svga_winsys_shader *src)
{
struct pipe_reference *src_ref;
struct pipe_reference *dst_ref;
struct vmw_svga_winsys_shader *dst;
if(pdst == NULL || *pdst == src)
return;
dst = *pdst;
src_ref = src ? &src->refcnt : NULL;
dst_ref = dst ? &dst->refcnt : NULL;
if (pipe_reference(dst_ref, src_ref)) {
struct svga_winsys_screen *sws = &dst->screen->base;
vmw_ioctl_shader_destroy(dst->screen, dst->shid);
#ifdef DEBUG
/* to detect dangling pointers */
assert(p_atomic_read(&dst->validated) == 0);
dst->shid = SVGA3D_INVALID_ID;
#endif
sws->buffer_destroy(sws, dst->buf);
FREE(dst);
}
*pdst = src;
}

View file

@ -0,0 +1,67 @@
/**********************************************************
* Copyright 2009-2012 VMware, Inc. All rights reserved.
*
* 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 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.
*
**********************************************************/
/**
* @file
* Shaders for VMware SVGA winsys.
*
* @author Jose Fonseca <jfonseca@vmware.com>
* @author Thomas Hellstrom <thellstrom@vmware.com>
*/
#ifndef VMW_SHADER_H_
#include "pipe/p_compiler.h"
#include "util/u_atomic.h"
#include "util/u_inlines.h"
struct vmw_svga_winsys_shader
{
int32_t validated;
struct pipe_reference refcnt;
struct vmw_winsys_screen *screen;
struct svga_winsys_buffer *buf;
uint32_t shid;
};
static INLINE struct svga_winsys_gb_shader *
svga_winsys_shader(struct vmw_svga_winsys_shader *shader)
{
assert(!shader || shader->shid != SVGA3D_INVALID_ID);
return (struct svga_winsys_gb_shader *)shader;
}
static INLINE struct vmw_svga_winsys_shader *
vmw_svga_winsys_shader(struct svga_winsys_gb_shader *shader)
{
return (struct vmw_svga_winsys_shader *)shader;
}
void
vmw_svga_winsys_shader_reference(struct vmw_svga_winsys_shader **pdst,
struct vmw_svga_winsys_shader *src);
#endif /* VMW_SHADER_H_ */

View file

@ -27,9 +27,152 @@
#include "svga_cmd.h"
#include "util/u_debug.h"
#include "util/u_memory.h"
#include "pipe/p_defines.h"
#include "vmw_surface.h"
#include "vmw_screen.h"
#include "vmw_buffer.h"
#include "vmw_context.h"
#include "pipebuffer/pb_bufmgr.h"
void *
vmw_svga_winsys_surface_map(struct svga_winsys_context *swc,
struct svga_winsys_surface *srf,
unsigned flags, boolean *retry)
{
struct vmw_svga_winsys_surface *vsrf = vmw_svga_winsys_surface(srf);
void *data = NULL;
struct pb_buffer *pb_buf;
uint32_t pb_flags;
struct vmw_winsys_screen *vws = vsrf->screen;
*retry = FALSE;
assert((flags & (PIPE_TRANSFER_READ | PIPE_TRANSFER_WRITE)) != 0);
pipe_mutex_lock(vsrf->mutex);
if (vsrf->mapcount) {
/*
* Only allow multiple readers to map.
*/
if ((flags & PIPE_TRANSFER_WRITE) ||
(vsrf->map_mode & PIPE_TRANSFER_WRITE))
goto out_unlock;
data = vsrf->data;
goto out_mapped;
}
vsrf->rebind = FALSE;
/*
* If we intend to read, there's no point discarding the
* data if busy.
*/
if (flags & PIPE_TRANSFER_READ || vsrf->shared)
flags &= ~PIPE_TRANSFER_DISCARD_WHOLE_RESOURCE;
/*
* Discard is a hint to a synchronized map.
*/
if (flags & PIPE_TRANSFER_DISCARD_WHOLE_RESOURCE)
flags &= ~PIPE_TRANSFER_UNSYNCHRONIZED;
/*
* The surface is allowed to be referenced on the command stream iff
* we're mapping unsynchronized or discard. This is an early check.
* We need to recheck after a failing discard map.
*/
if (!(flags & (PIPE_TRANSFER_DISCARD_WHOLE_RESOURCE |
PIPE_TRANSFER_UNSYNCHRONIZED)) &&
p_atomic_read(&vsrf->validated)) {
*retry = TRUE;
goto out_unlock;
}
pb_flags = flags & (PIPE_TRANSFER_READ_WRITE | PIPE_TRANSFER_UNSYNCHRONIZED);
if (flags & PIPE_TRANSFER_DISCARD_WHOLE_RESOURCE) {
struct pb_manager *provider;
struct pb_desc desc;
/*
* First, if possible, try to map existing storage with DONTBLOCK.
*/
if (!p_atomic_read(&vsrf->validated)) {
data = vmw_svga_winsys_buffer_map(&vws->base, vsrf->buf,
PIPE_TRANSFER_DONTBLOCK | pb_flags);
if (data)
goto out_mapped;
}
/*
* Attempt to get a new buffer.
*/
provider = vws->pools.mob_fenced;
memset(&desc, 0, sizeof(desc));
desc.alignment = 4096;
pb_buf = provider->create_buffer(provider, vsrf->size, &desc);
if (pb_buf != NULL) {
struct svga_winsys_buffer *vbuf =
vmw_svga_winsys_buffer_wrap(pb_buf);
data = vmw_svga_winsys_buffer_map(&vws->base, vbuf, pb_flags);
if (data) {
vsrf->rebind = TRUE;
/*
* We've discarded data on this surface and thus
* it's data is no longer consider referenced.
*/
vmw_swc_surface_clear_reference(swc, vsrf);
if (vsrf->buf)
vmw_svga_winsys_buffer_destroy(&vws->base, vsrf->buf);
vsrf->buf = vbuf;
goto out_mapped;
} else
vmw_svga_winsys_buffer_destroy(&vws->base, vbuf);
}
/*
* We couldn't get and map a new buffer for some reason.
* Fall through to an ordinary map.
* But tell pipe driver to flush now if already on validate list,
* Otherwise we'll overwrite previous contents.
*/
if (!(flags & PIPE_TRANSFER_UNSYNCHRONIZED) &&
p_atomic_read(&vsrf->validated)) {
*retry = TRUE;
goto out_unlock;
}
}
pb_flags |= (flags & PIPE_TRANSFER_DONTBLOCK);
data = vmw_svga_winsys_buffer_map(&vws->base, vsrf->buf, pb_flags);
if (data == NULL)
goto out_unlock;
out_mapped:
++vsrf->mapcount;
vsrf->data = data;
vsrf->map_mode = flags & (PIPE_TRANSFER_READ | PIPE_TRANSFER_WRITE);
out_unlock:
pipe_mutex_unlock(vsrf->mutex);
return data;
}
void
vmw_svga_winsys_surface_unmap(struct svga_winsys_context *swc,
struct svga_winsys_surface *srf,
boolean *rebind)
{
struct vmw_svga_winsys_surface *vsrf = vmw_svga_winsys_surface(srf);
pipe_mutex_lock(vsrf->mutex);
if (--vsrf->mapcount == 0) {
*rebind = vsrf->rebind;
vsrf->rebind = FALSE;
vmw_svga_winsys_buffer_unmap(&vsrf->screen->base, vsrf->buf);
}
pipe_mutex_unlock(vsrf->mutex);
}
void
vmw_svga_winsys_surface_reference(struct vmw_svga_winsys_surface **pdst,
@ -48,12 +191,15 @@ vmw_svga_winsys_surface_reference(struct vmw_svga_winsys_surface **pdst,
dst_ref = dst ? &dst->refcnt : NULL;
if (pipe_reference(dst_ref, src_ref)) {
if (dst->buf)
vmw_svga_winsys_buffer_destroy(&dst->screen->base, dst->buf);
vmw_ioctl_surface_destroy(dst->screen, dst->sid);
#ifdef DEBUG
/* to detect dangling pointers */
assert(p_atomic_read(&dst->validated) == 0);
dst->sid = SVGA3D_INVALID_ID;
#endif
pipe_mutex_destroy(dst->mutex);
FREE(dst);
}

View file

@ -38,6 +38,8 @@
#include "pipe/p_compiler.h"
#include "util/u_atomic.h"
#include "util/u_inlines.h"
#include "os/os_thread.h"
#include "pipebuffer/pb_buffer.h"
#define VMW_MAX_PRESENTS 3
@ -54,6 +56,15 @@ struct vmw_svga_winsys_surface
/* FIXME: make this thread safe */
unsigned next_present_no;
uint32_t present_fences[VMW_MAX_PRESENTS];
pipe_mutex mutex;
struct svga_winsys_buffer *buf; /* Current backing guest buffer */
uint32_t mapcount; /* Number of mappers */
uint32_t map_mode; /* PIPE_TRANSFER_[READ|WRITE] */
void *data; /* Pointer to data if mapcount != 0*/
boolean shared; /* Shared surface. Never discard */
uint32_t size; /* Size of backing buffer */
boolean rebind; /* Surface needs a rebind after next unmap */
};
@ -75,5 +86,13 @@ vmw_svga_winsys_surface(struct svga_winsys_surface *surf)
void
vmw_svga_winsys_surface_reference(struct vmw_svga_winsys_surface **pdst,
struct vmw_svga_winsys_surface *src);
void *
vmw_svga_winsys_surface_map(struct svga_winsys_context *swc,
struct svga_winsys_surface *srf,
unsigned flags, boolean *retry);
void
vmw_svga_winsys_surface_unmap(struct svga_winsys_context *swc,
struct svga_winsys_surface *srf,
boolean *rebind);
#endif /* VMW_SURFACE_H_ */

View file

@ -28,6 +28,10 @@
#ifndef __VMWGFX_DRM_H__
#define __VMWGFX_DRM_H__
#ifndef __KERNEL__
#include <drm.h>
#endif
#define DRM_VMW_MAX_SURFACE_FACES 6
#define DRM_VMW_MAX_MIP_LEVELS 24
@ -54,7 +58,12 @@
#define DRM_VMW_FENCE_EVENT 17
#define DRM_VMW_PRESENT 18
#define DRM_VMW_PRESENT_READBACK 19
#define DRM_VMW_UPDATE_LAYOUT 20
#define DRM_VMW_CREATE_SHADER 21
#define DRM_VMW_UNREF_SHADER 22
#define DRM_VMW_GB_SURFACE_CREATE 23
#define DRM_VMW_GB_SURFACE_REF 24
#define DRM_VMW_SYNCCPU 25
/*************************************************************************/
/**
@ -75,6 +84,9 @@
#define DRM_VMW_PARAM_FIFO_CAPS 4
#define DRM_VMW_PARAM_MAX_FB_SIZE 5
#define DRM_VMW_PARAM_FIFO_HW_VERSION 6
#define DRM_VMW_PARAM_MAX_SURF_MEMORY 7
#define DRM_VMW_PARAM_3D_CAPS_SIZE 8
#define DRM_VMW_PARAM_MAX_MOB_MEMORY 9
/**
* struct drm_vmw_getparam_arg
@ -659,6 +671,51 @@ struct drm_vmw_fence_arg {
};
/*************************************************************************/
/**
* DRM_VMW_FENCE_EVENT
*
* Queues an event on a fence to be delivered on the drm character device
* when the fence has signaled the DRM_VMW_FENCE_FLAG_EXEC flag.
* Optionally the approximate time when the fence signaled is
* given by the event.
*/
/*
* The event type
*/
#define DRM_VMW_EVENT_FENCE_SIGNALED 0x80000000
struct drm_vmw_event_fence {
struct drm_event base;
uint64_t user_data;
uint32_t tv_sec;
uint32_t tv_usec;
};
/*
* Flags that may be given to the command.
*/
/* Request fence signaled time on the event. */
#define DRM_VMW_FE_FLAG_REQ_TIME (1 << 0)
/**
* struct drm_vmw_fence_event_arg
*
* @fence_rep: Pointer to fence_rep structure cast to uint64_t or 0 if
* the fence is not supposed to be referenced by user-space.
* @user_info: Info to be delivered with the event.
* @handle: Attach the event to this fence only.
* @flags: A set of flags as defined above.
*/
struct drm_vmw_fence_event_arg {
uint64_t fence_rep;
uint64_t user_data;
uint32_t handle;
uint32_t flags;
};
/*************************************************************************/
/**
* DRM_VMW_PRESENT
@ -720,5 +777,276 @@ struct drm_vmw_present_readback_arg {
uint64_t fence_rep;
};
/*************************************************************************/
/**
* DRM_VMW_UPDATE_LAYOUT - Update layout
*
* Updates the preferred modes and connection status for connectors. The
* command consists of one drm_vmw_update_layout_arg pointing to an array
* of num_outputs drm_vmw_rect's.
*/
/**
* struct drm_vmw_update_layout_arg
*
* @num_outputs: number of active connectors
* @rects: pointer to array of drm_vmw_rect cast to an uint64_t
*
* Input argument to the DRM_VMW_UPDATE_LAYOUT Ioctl.
*/
struct drm_vmw_update_layout_arg {
uint32_t num_outputs;
uint32_t pad64;
uint64_t rects;
};
/*************************************************************************/
/**
* DRM_VMW_CREATE_SHADER - Create shader
*
* Creates a shader and optionally binds it to a dma buffer containing
* the shader byte-code.
*/
/**
* enum drm_vmw_shader_type - Shader types
*/
enum drm_vmw_shader_type {
drm_vmw_shader_type_vs = 0,
drm_vmw_shader_type_ps,
drm_vmw_shader_type_gs
};
/**
* struct drm_vmw_shader_create_arg
*
* @shader_type: Shader type of the shader to create.
* @size: Size of the byte-code in bytes.
* where the shader byte-code starts
* @buffer_handle: Buffer handle identifying the buffer containing the
* shader byte-code
* @shader_handle: On successful completion contains a handle that
* can be used to subsequently identify the shader.
* @offset: Offset in bytes into the buffer given by @buffer_handle,
*
* Input / Output argument to the DRM_VMW_CREATE_SHADER Ioctl.
*/
struct drm_vmw_shader_create_arg {
enum drm_vmw_shader_type shader_type;
uint32_t size;
uint32_t buffer_handle;
uint32_t shader_handle;
uint64_t offset;
};
/*************************************************************************/
/**
* DRM_VMW_UNREF_SHADER - Unreferences a shader
*
* Destroys a user-space reference to a shader, optionally destroying
* it.
*/
/**
* struct drm_vmw_shader_arg
*
* @handle: Handle identifying the shader to destroy.
*
* Input argument to the DRM_VMW_UNREF_SHADER ioctl.
*/
struct drm_vmw_shader_arg {
uint32_t handle;
uint32_t pad64;
};
/*************************************************************************/
/**
* DRM_VMW_GB_SURFACE_CREATE - Create a host guest-backed surface.
*
* Allocates a surface handle and queues a create surface command
* for the host on the first use of the surface. The surface ID can
* be used as the surface ID in commands referencing the surface.
*/
/**
* enum drm_vmw_surface_flags
*
* @drm_vmw_surface_flag_shareable: Whether the surface is shareable
* @drm_vmw_surface_flag_scanout: Whether the surface is a scanout
* surface.
* @drm_vmw_surface_flag_create_buffer: Create a backup buffer if none is
* given.
*/
enum drm_vmw_surface_flags {
drm_vmw_surface_flag_shareable = (1 << 0),
drm_vmw_surface_flag_scanout = (1 << 1),
drm_vmw_surface_flag_create_buffer = (1 << 2)
};
/**
* struct drm_vmw_gb_surface_create_req
*
* @svga3d_flags: SVGA3d surface flags for the device.
* @format: SVGA3d format.
* @mip_level: Number of mip levels for all faces.
* @drm_surface_flags Flags as described above.
* @multisample_count Future use. Set to 0.
* @autogen_filter Future use. Set to 0.
* @buffer_handle Buffer handle of backup buffer. SVGA3D_INVALID_ID
* if none.
* @base_size Size of the base mip level for all faces.
*
* Input argument to the DRM_VMW_GB_SURFACE_CREATE Ioctl.
* Part of output argument for the DRM_VMW_GB_SURFACE_REF Ioctl.
*/
struct drm_vmw_gb_surface_create_req {
uint32_t svga3d_flags;
uint32_t format;
uint32_t mip_levels;
enum drm_vmw_surface_flags drm_surface_flags;
uint32_t multisample_count;
uint32_t autogen_filter;
uint32_t buffer_handle;
uint32_t pad64;
struct drm_vmw_size base_size;
};
/**
* struct drm_vmw_gb_surface_create_rep
*
* @handle: Surface handle.
* @backup_size: Size of backup buffers for this surface.
* @buffer_handle: Handle of backup buffer. SVGA3D_INVALID_ID if none.
* @buffer_size: Actual size of the buffer identified by
* @buffer_handle
* @buffer_map_handle: Offset into device address space for the buffer
* identified by @buffer_handle.
*
* Part of output argument for the DRM_VMW_GB_SURFACE_REF ioctl.
* Output argument for the DRM_VMW_GB_SURFACE_CREATE ioctl.
*/
struct drm_vmw_gb_surface_create_rep {
uint32_t handle;
uint32_t backup_size;
uint32_t buffer_handle;
uint32_t buffer_size;
uint64_t buffer_map_handle;
};
/**
* union drm_vmw_gb_surface_create_arg
*
* @req: Input argument as described above.
* @rep: Output argument as described above.
*
* Argument to the DRM_VMW_GB_SURFACE_CREATE ioctl.
*/
union drm_vmw_gb_surface_create_arg {
struct drm_vmw_gb_surface_create_rep rep;
struct drm_vmw_gb_surface_create_req req;
};
/*************************************************************************/
/**
* DRM_VMW_GB_SURFACE_REF - Reference a host surface.
*
* Puts a reference on a host surface with a given handle, as previously
* returned by the DRM_VMW_GB_SURFACE_CREATE ioctl.
* A reference will make sure the surface isn't destroyed while we hold
* it and will allow the calling client to use the surface handle in
* the command stream.
*
* On successful return, the Ioctl returns the surface information given
* to and returned from the DRM_VMW_GB_SURFACE_CREATE ioctl.
*/
/**
* struct drm_vmw_gb_surface_reference_arg
*
* @creq: The data used as input when the surface was created, as described
* above at "struct drm_vmw_gb_surface_create_req"
* @crep: Additional data output when the surface was created, as described
* above at "struct drm_vmw_gb_surface_create_rep"
*
* Output Argument to the DRM_VMW_GB_SURFACE_REF ioctl.
*/
struct drm_vmw_gb_surface_ref_rep {
struct drm_vmw_gb_surface_create_req creq;
struct drm_vmw_gb_surface_create_rep crep;
};
/**
* union drm_vmw_gb_surface_reference_arg
*
* @req: Input data as described above at "struct drm_vmw_surface_arg"
* @rep: Output data as described above at "struct drm_vmw_gb_surface_ref_rep"
*
* Argument to the DRM_VMW_GB_SURFACE_REF Ioctl.
*/
union drm_vmw_gb_surface_reference_arg {
struct drm_vmw_gb_surface_ref_rep rep;
struct drm_vmw_surface_arg req;
};
/*************************************************************************/
/**
* DRM_VMW_SYNCCPU - Sync a DMA buffer / MOB for CPU access.
*
* Idles any previously submitted GPU operations on the buffer and
* by default blocks command submissions that reference the buffer.
* If the file descriptor used to grab a blocking CPU sync is closed, the
* cpu sync is released.
* The flags argument indicates how the grab / release operation should be
* performed:
*/
/**
* enum drm_vmw_synccpu_flags - Synccpu flags:
*
* @drm_vmw_synccpu_read: Sync for read. If sync is done for read only, it's a
* hint to the kernel to allow command submissions that references the buffer
* for read-only.
* @drm_vmw_synccpu_write: Sync for write. Block all command submissions
* referencing this buffer.
* @drm_vmw_synccpu_dontblock: Dont wait for GPU idle, but rather return
* -EBUSY should the buffer be busy.
* @drm_vmw_synccpu_allow_cs: Allow command submission that touches the buffer
* while the buffer is synced for CPU. This is similar to the GEM bo idle
* behavior.
*/
enum drm_vmw_synccpu_flags {
drm_vmw_synccpu_read = (1 << 0),
drm_vmw_synccpu_write = (1 << 1),
drm_vmw_synccpu_dontblock = (1 << 2),
drm_vmw_synccpu_allow_cs = (1 << 3)
};
/**
* enum drm_vmw_synccpu_op - Synccpu operations:
*
* @drm_vmw_synccpu_grab: Grab the buffer for CPU operations
* @drm_vmw_synccpu_release: Release a previous grab.
*/
enum drm_vmw_synccpu_op {
drm_vmw_synccpu_grab,
drm_vmw_synccpu_release
};
/**
* struct drm_vmw_synccpu_arg
*
* @op: The synccpu operation as described above.
* @handle: Handle identifying the buffer object.
* @flags: Flags as described above.
*/
struct drm_vmw_synccpu_arg {
enum drm_vmw_synccpu_op op;
enum drm_vmw_synccpu_flags flags;
uint32_t handle;
uint32_t pad64;
};
#endif