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>
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
--- /dev/null
+/**************************************************************************
+ *
+ * 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;
+}
#include "vmw_screen.h"
#include "vmw_buffer.h"
-
struct vmw_gmr_bufmgr;
struct vmw_region *region;
void *map;
+ unsigned map_flags;
};
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;
}
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);
+ }
}
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:
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));
+}
#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;
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)
{
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);
#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"
#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
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 {
} 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;
} 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
/* 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))
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)
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);
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);
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;
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_context_relocation(struct svga_winsys_context *swc,
+ uint32 *cid)
+{
+ *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,
+ struct SVGAGuestPtr *where,
+ 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->region.where = where;
+
+ /*
+ * 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 = 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,
struct svga_winsys_surface *surface,
unsigned flags)
{
- struct vmw_svga_winsys_context *vswc = vmw_svga_winsys_context(swc);
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;
}
- assert(vswc->surface.staged < vswc->surface.reserved);
-
vsurf = vmw_svga_winsys_surface(surface);
+ vmw_swc_surface_only_relocation(swc, where, vsurf, flags);
- *where = vsurf->sid;
+ if (swc->have_gb_objects && vsurf->buf != NULL) {
- vmw_svga_winsys_surface_reference(&vswc->surface.handles[vswc->surface.used + vswc->surface.staged], vsurf);
- p_atomic_inc(&vsurf->validated);
- ++vswc->surface.staged;
-}
+ /*
+ * 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_region_relocation(struct svga_winsys_context *swc,
- struct SVGAGuestPtr *where,
- struct svga_winsys_buffer *buffer,
- uint32 offset,
- unsigned flags)
+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_region_relocation *reloc;
- unsigned translated_flags;
- enum pipe_error ret;
-
- assert(vswc->region.staged < vswc->region.reserved);
+ struct vmw_svga_winsys_shader *vshader;
+ struct vmw_ctx_validate_item *ishader;
+ if(!shader) {
+ *shid = SVGA3D_INVALID_ID;
+ return;
+ }
- reloc = &vswc->region.relocs[vswc->region.used + vswc->region.staged];
- reloc->where = where;
- pb_reference(&reloc->buffer, vmw_pb_buffer(buffer));
- reloc->offset = offset;
+ 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;
+ }
- ++vswc->region.staged;
+ if (!ishader->referenced) {
+ ishader->referenced = TRUE;
+ p_atomic_inc(&vshader->validated);
+ }
- 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;
+ *shid = vshader->shid;
- /*
- * 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.
- */
- vswc->seen_regions += reloc->buffer->size;
- if(vswc->seen_regions >= VMW_GMR_POOL_SIZE/3)
- vswc->preemptive_flush = TRUE;
+ 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)
{
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;
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)
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;
}
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_ */
* 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"
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_seq_is_signaled - Check whether a fence seqno is
+ * signaled.
+ *
+ * @ops: Pointer to a struct pb_fence_ops.
+ *
+ */
+static INLINE boolean
+vmw_fence_seq_is_signaled(uint32_t seq, uint32_t last, uint32_t cur)
+{
+ return (cur - last <= cur - seq);
+}
+
+
+/**
+ * vmw_fence_ops - Return the vmw_fence_ops structure backing a
+ * struct pb_fence_ops pointer.
+ *
+ * @ops: Pointer to a struct pb_fence_ops.
+ *
+ */
+static INLINE struct vmw_fence_ops *
+vmw_fence_ops(struct pb_fence_ops *ops)
+{
+ assert(ops);
+ return (struct vmw_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 *
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(uint32_t handle, uint32_t mask)
+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);
+ }
- return (struct pipe_fence_handle *) fence;
-}
+ pipe_mutex_unlock(ops->mutex);
-/**
- * vmw_fence_ops - Return the vmw_fence_ops structure backing a
- * struct pb_fence_ops pointer.
- *
- * @ops: Pointer to a struct pb_fence_ops.
- *
- */
-static INLINE struct vmw_fence_ops *
-vmw_fence_ops(struct pb_fence_ops *ops)
-{
- assert(ops);
- return (struct vmw_fence_ops *)ops;
+ return (struct pipe_fence_handle *) fence;
}
-
/**
* vmw_fence_reference - Reference / unreference a vmw fence object.
*
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);
}
}
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
}
/**
static void
vmw_fence_ops_destroy(struct pb_fence_ops *ops)
{
+ vmw_fences_release(vmw_fence_ops(ops));
FREE(ops);
}
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;
return &ops->base;
}
-
-
#include "pipe/p_compiler.h"
-
+#include "pipebuffer/pb_buffer_fenced.h"
struct pipe_fence_handle;
struct pb_fence_ops;
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,
#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;
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);
+ }
}
#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
struct pb_manager;
struct vmw_region;
+struct vmw_cap_3d {
+ boolean has_cap;
+ SVGA3dDevCapResult result;
+};
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 {
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;
};
}
/* */
+uint32_t
+vmw_region_size(struct vmw_region *region);
+
uint32
vmw_ioctl_context_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,
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);
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_ */
#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"
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,
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;
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,
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;
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));
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:
#include "xf86drm.h"
#include "vmwgfx_drm.h"
#include "svga3d_caps.h"
+#include "svga3d_reg.h"
#include "os/os_mman.h"
struct vmw_region
{
- SVGAGuestPtr ptr;
uint32_t handle;
uint64_t map_handle;
void *data;
*/
#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)
{
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)
{
*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.
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;
SVGAGuestPtr
vmw_ioctl_region_ptr(struct vmw_region *region)
{
- return region->ptr;
+ SVGAGuestPtr ptr = {region->handle, 0};
+ return ptr;
}
void *
--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)
if (ret != 0)
return ret;
+ vmw_fences_signal(vws->fence_ops, arg.passed_seqno, 0, FALSE);
+
return (arg.signaled) ? 0 : -1;
}
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)
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,
}
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,
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;
}
#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.
*
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);
}
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;
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;
}
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
* 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);
-
- if (!vws->pools.gmr_slab_fenced)
- goto error;
+
+ 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;
+
+ 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;
vmw_pools_cleanup(vws);
return FALSE;
}
-
#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,
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);
}
{
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;
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;
{
struct vmw_winsys_screen *vws = vmw_winsys_screen(sws);
+ if (sws->have_gb_objects)
+ return SVGA3D_HWVERSION_WS8_B1;
+
return (SVGA3dHardwareVersion) vws->ioctl.hwversion;
}
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)
- 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)
+ if (index > vws->ioctl.num_cap_3d || !vws->ioctl.cap_3d[index].has_cap)
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;
+ *result = vws->ioctl.cap_3d[index].result;
+ return TRUE;
+}
- if ((SVGA3dDevCapIndex) capArray[mid][0] == index) {
- /*
- * Found it.
- */
- result->u = capArray[mid][1];
- 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;
+}
- /*
- * Divide and conquer.
- */
- if ((SVGA3dDevCapIndex) capArray[mid][0] > index) {
- last = mid - 1;
- } else {
- first = mid + 1;
- }
- }
+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);
- return FALSE;
+ vmw_svga_winsys_shader_reference(&d_shader, NULL);
}
-
boolean
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;
--- /dev/null
+/**********************************************************
+ * 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;
+}
--- /dev/null
+/**********************************************************
+ * 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_ */
#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,
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);
}
#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
/* 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 */
};
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_ */
#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
#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
/*************************************************************************/
/**
#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
};
+/*************************************************************************/
+/**
+ * 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
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