/**************************************************************************
*
* Copyright 2006 Tungsten Graphics, Inc., Cedar Park, Texas.
* 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 TUNGSTEN GRAPHICS 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.
*
**************************************************************************/
/* Originally a fake version of the buffer manager so that we can
* prototype the changes in a driver fairly quickly, has been fleshed
* out to a fully functional interim solution.
*
* Basically wraps the old style memory management in the new
* programming interface, but is more expressive and avoids many of
* the bugs in the old texture manager.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <errno.h>
#include <xf86drm.h>
#include <pthread.h>
#include "intel_bufmgr.h"
#include "intel_bufmgr_priv.h"
#include "drm.h"
#include "i915_drm.h"
#include "mm.h"
#include "libdrm_lists.h"
#define DBG(...) do { \
if (bufmgr_fake->bufmgr.debug) \
drmMsg(__VA_ARGS__); \
} while (0)
/* Internal flags:
*/
#define BM_NO_BACKING_STORE 0x00000001
#define BM_NO_FENCE_SUBDATA 0x00000002
#define BM_PINNED 0x00000004
/* Wrapper around mm.c's mem_block, which understands that you must
* wait for fences to expire before memory can be freed. This is
* specific to our use of memcpy for uploads - an upload that was
* processed through the command queue wouldn't need to care about
* fences.
*/
#define MAX_RELOCS 4096
struct fake_buffer_reloc {
/** Buffer object that the relocation points at. */
drm_intel_bo *target_buf;
/** Offset of the relocation entry within reloc_buf. */
uint32_t offset;
/**
* Cached value of the offset when we last performed this relocation.
*/
uint32_t last_target_offset;
/** Value added to target_buf's offset to get the relocation entry. */
uint32_t delta;
/** Cache domains the target buffer is read into. */
uint32_t read_domains;
/** Cache domain the target buffer will have dirty cachelines in. */
uint32_t write_domain;
};
struct block {
struct block *next, *prev;
struct mem_block *mem; /* BM_MEM_AGP */
/**
* Marks that the block is currently in the aperture and has yet to be
* fenced.
*/
unsigned on_hardware:1;
/**
* Marks that the block is currently fenced (being used by rendering)
* and can't be freed until @fence is passed.
*/
unsigned fenced:1;
/** Fence cookie for the block. */
unsigned fence; /* Split to read_fence, write_fence */
drm_intel_bo *bo;
void *virtual;
};
typedef struct _bufmgr_fake {
drm_intel_bufmgr bufmgr;
pthread_mutex_t lock;
unsigned long low_offset;
unsigned long size;
void *virtual;
struct mem_block *heap;
unsigned buf_nr; /* for generating ids */
/**
* List of blocks which are currently in the GART but haven't been
* fenced yet.
*/
struct block on_hardware;
/**
* List of blocks which are in the GART and have an active fence on
* them.
*/
struct block fenced;
/**
* List of blocks which have an expired fence and are ready to be
* evicted.
*/
struct block lru;
unsigned int last_fence;
unsigned fail:1;
unsigned need_fence:1;
int thrashing;
/**
* Driver callback to emit a fence, returning the cookie.
*
* This allows the driver to hook in a replacement for the DRM usage in
* bufmgr_fake.
*
* Currently, this also requires that a write flush be emitted before
* emitting the fence, but this should change.
*/
unsigned int (*fence_emit) (void *private);
/** Driver callback to wait for a fence cookie to have passed. */
void (*fence_wait) (unsigned int fence, void *private);
void *fence_priv;
/**
* Driver callback to execute a buffer.
*
* This allows the driver to hook in a replacement for the DRM usage in
* bufmgr_fake.
*/
int (*exec) (drm_intel_bo *bo, unsigned int used, void *priv);
void *exec_priv;
/** Driver-supplied argument to driver callbacks */
void *driver_priv;
/**
* Pointer to kernel-updated sarea data for the last completed user irq
*/
volatile int *last_dispatch;
int fd;
int debug;
int performed_rendering;
} drm_intel_bufmgr_fake;
typedef struct _drm_intel_bo_fake {
drm_intel_bo bo;
unsigned id; /* debug only */
const char *name;
unsigned dirty:1;
/**
* has the card written to this buffer - we make need to copy it back
*/
unsigned card_dirty:1;
unsigned int refcount;
/* Flags may consist of any of the DRM_BO flags, plus
* DRM_BO_NO_BACKING_STORE and BM_NO_FENCE_SUBDATA, which are the
* first two driver private flags.
*/
uint64_t flags;
/** Cache domains the target buffer is read into. */
uint32_t read_domains;
/** Cache domain the target buffer will have dirty cachelines in. */
uint32_t write_domain;
unsigned int alignment;
int is_static, validated;
unsigned int map_count;
/** relocation list */
struct fake_buffer_reloc *relocs;
int nr_relocs;
/**
* Total size of the target_bos of this buffer.
*
* Used for estimation in check_aperture.
*/
unsigned int child_size;
struct block *block;
void *backing_store;
void (*invalidate_cb) (drm_intel_bo *bo, void *ptr);
void *invalidate_ptr;
} drm_intel_bo_fake;
static int clear_fenced(drm_intel_bufmgr_fake *bufmgr_fake,
unsigned int fence_cookie);
#define MAXFENCE 0x7fffffff
static int
FENCE_LTE(unsigned a, unsigned b)
{
if (a == b)
return 1;
if (a < b && b - a < (1 << 24))
return 1;
if (a > b && MAXFENCE - a + b < (1 << 24))
return 1;
return 0;
}
void
drm_intel_bufmgr_fake_set_fence_callback(drm_intel_bufmgr *bufmgr,
unsigned int (*emit) (void *priv),
void (*wait) (unsigned int fence,
void *priv),
void *priv)
{
drm_intel_bufmgr_fake *bufmgr_fake = (drm_intel_bufmgr_fake *) bufmgr;
bufmgr_fake->fence_emit = emit;
bufmgr_fake->fence_wait = wait;
bufmgr_fake->fence_priv = priv;
}
static unsigned int
_fence_emit_internal(drm_intel_bufmgr_fake *bufmgr_fake)
{
struct drm_i915_irq_emit ie;
int ret, seq = 1;
if (bufmgr_fake->fence_emit != NULL) {
seq = bufmgr_fake->fence_emit(bufmgr_fake->fence_priv);
return seq;
}
ie.irq_seq = &seq;
ret = drmCommandWriteRead(bufmgr_fake->fd, DRM_I915_IRQ_EMIT,
|