mesa-drm/libdrm/intel/intel_bufmgr_fake.c
Eric Anholt 4b9826408f intel: Rename dri_ and intel_ symbols to drm_intel_.
I wanted to avoid doing this, as it's a bunch of churn, but there was a
conflict between the dri_ symbols in libdrm and the symbols that were in
Mesa in 7.2, which broke Mesa 7.2 AIGLX when the 2D driver had loaded new
libdrm symbols.  The new naming was recommended by cworth for giving the
code a unique prefix identifying where the code lives.

Additionally, take the opportunity to fix up two API mistakes: emit_reloc's
arguments were in a nonsensical order, and set_tiling lacked the stride
argument that the kernel will want to use soon.  API compatibility with
released code is maintained using #defines.
2008-10-30 11:29:40 -07:00

1522 lines
42 KiB
C

/**************************************************************************
*
* 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 ALIGN(value, alignment) ((value + alignment - 1) & ~(alignment - 1))
#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,
&ie, sizeof(ie));
if (ret) {
drmMsg("%s: drm_i915_irq_emit: %d\n", __FUNCTION__, ret);
abort();
}
DBG("emit 0x%08x\n", seq);
return seq;
}
static void
_fence_wait_internal(drm_intel_bufmgr_fake *bufmgr_fake, int seq)
{
struct drm_i915_irq_wait iw;
int hw_seq, busy_count = 0;
int ret;
int kernel_lied;
if (bufmgr_fake->fence_wait != NULL) {
bufmgr_fake->fence_wait(seq, bufmgr_fake->fence_priv);
clear_fenced(bufmgr_fake, seq);
return;
}
DBG("wait 0x%08x\n", iw.irq_seq);
iw.irq_seq = seq;
/* The kernel IRQ_WAIT implementation is all sorts of broken.
* 1) It returns 1 to 0x7fffffff instead of using the full 32-bit unsigned
* range.
* 2) It returns 0 if hw_seq >= seq, not seq - hw_seq < 0 on the 32-bit
* signed range.
* 3) It waits if seq < hw_seq, not seq - hw_seq > 0 on the 32-bit
* signed range.
* 4) It returns -EBUSY in 3 seconds even if the hardware is still
* successfully chewing through buffers.
*
* Assume that in userland we treat sequence numbers as ints, which makes
* some of the comparisons convenient, since the sequence numbers are
* all postive signed integers.
*
* From this we get several cases we need to handle. Here's a timeline.
* 0x2 0x7 0x7ffffff8 0x7ffffffd
* | | | |
* -------------------------------------------------------------------
*
* A) Normal wait for hw to catch up
* hw_seq seq
* | |
* -------------------------------------------------------------------
* seq - hw_seq = 5. If we call IRQ_WAIT, it will wait for hw to catch up.
*
* B) Normal wait for a sequence number that's already passed.
* seq hw_seq
* | |
* -------------------------------------------------------------------
* seq - hw_seq = -5. If we call IRQ_WAIT, it returns 0 quickly.
*
* C) Hardware has already wrapped around ahead of us
* hw_seq seq
* | |
* -------------------------------------------------------------------
* seq - hw_seq = 0x80000000 - 5. If we called IRQ_WAIT, it would wait
* for hw_seq >= seq, which may never occur. Thus, we want to catch this
* in userland and return 0.
*
* D) We've wrapped around ahead of the hardware.
* seq hw_seq
* | |
* -------------------------------------------------------------------
* seq - hw_seq = -(0x80000000 - 5). If we called IRQ_WAIT, it would return
* 0 quickly because hw_seq >= seq, even though the hardware isn't caught up.
* Thus, we need to catch this early return in userland and bother the
* kernel until the hardware really does catch up.
*
* E) Hardware might wrap after we test in userland.
* hw_seq seq
* | |
* -------------------------------------------------------------------
* seq - hw_seq = 5. If we call IRQ_WAIT, it will likely see seq >= hw_seq
* and wait. However, suppose hw_seq wraps before we make it into the
* kernel. The kernel sees hw_seq >= seq and waits for 3 seconds then
* returns -EBUSY. This is case C). We should catch this and then return
* successfully.
*
* F) Hardware might take a long time on a buffer.
* hw_seq seq
* | |
* -------------------------------------------------------------------
* seq - hw_seq = 5. If we call IRQ_WAIT, if sequence 2 through 5 take too
* long, it will return -EBUSY. Batchbuffers in the gltestperf demo were
* seen to take up to 7 seconds. We should catch early -EBUSY return
* and keep trying.
*/
do {
/* Keep a copy of last_dispatch so that if the wait -EBUSYs because the
* hardware didn't catch up in 3 seconds, we can see if it at least made
* progress and retry.
*/
hw_seq = *bufmgr_fake->last_dispatch;
/* Catch case C */
if (seq - hw_seq > 0x40000000)
return;
ret = drmCommandWrite(bufmgr_fake->fd, DRM_I915_IRQ_WAIT,
&iw, sizeof(iw));
/* Catch case D */
kernel_lied = (ret == 0) && (seq - *bufmgr_fake->last_dispatch <
-0x40000000);
/* Catch case E */
if (ret == -EBUSY && (seq - *bufmgr_fake->last_dispatch > 0x40000000))
ret = 0;
/* Catch case F: Allow up to 15 seconds chewing on one buffer. */
if ((ret == -EBUSY) && (hw_seq != *bufmgr_fake->last_dispatch))
busy_count = 0;
else
busy_count++;
} while (kernel_lied || ret == -EAGAIN || ret == -EINTR ||
(ret == -EBUSY && busy_count < 5));
if (ret != 0) {
drmMsg("%s:%d: Error waiting for fence: %s.\n", __FILE__, __LINE__,
strerror(-ret));
abort();
}
clear_fenced(bufmgr_fake, seq);
}
static int
_fence_test(drm_intel_bufmgr_fake *bufmgr_fake, unsigned fence)
{
/* Slight problem with wrap-around:
*/
return fence == 0 || FENCE_LTE(fence, bufmgr_fake->last_fence);
}
/**
* Allocate a memory manager block for the buffer.
*/
static int
alloc_block(drm_intel_bo *bo)
{
drm_intel_bo_fake *bo_fake = (drm_intel_bo_fake *)bo;
drm_intel_bufmgr_fake *bufmgr_fake= (drm_intel_bufmgr_fake *)bo->bufmgr;
struct block *block = (struct block *)calloc(sizeof *block, 1);
unsigned int align_log2 = ffs(bo_fake->alignment) - 1;
unsigned int sz;
if (!block)
return 1;
sz = (bo->size + bo_fake->alignment - 1) & ~(bo_fake->alignment - 1);
block->mem = mmAllocMem(bufmgr_fake->heap, sz, align_log2, 0);
if (!block->mem) {
free(block);
return 0;
}
DRMINITLISTHEAD(block);
/* Insert at head or at tail???
*/
DRMLISTADDTAIL(block, &bufmgr_fake->lru);
block->virtual = (uint8_t *)bufmgr_fake->virtual +
block->mem->ofs - bufmgr_fake->low_offset;
block->bo = bo;
bo_fake->block = block;
return 1;
}
/* Release the card storage associated with buf:
*/
static void free_block(drm_intel_bufmgr_fake *bufmgr_fake, struct block *block)
{
drm_intel_bo_fake *bo_fake;
DBG("free block %p %08x %d %d\n", block, block->mem->ofs, block->on_hardware, block->fenced);
if (!block)
return;
bo_fake = (drm_intel_bo_fake *)block->bo;
if (!(bo_fake->flags & (BM_PINNED | BM_NO_BACKING_STORE)) && (bo_fake->card_dirty == 1)) {
memcpy(bo_fake->backing_store, block->virtual, block->bo->size);
bo_fake->card_dirty = 0;
bo_fake->dirty = 1;
}
if (block->on_hardware) {
block->bo = NULL;
}
else if (block->fenced) {
block->bo = NULL;
}
else {
DBG(" - free immediately\n");
DRMLISTDEL(block);
mmFreeMem(block->mem);
free(block);
}
}
static void
alloc_backing_store(drm_intel_bo *bo)
{
drm_intel_bufmgr_fake *bufmgr_fake = (drm_intel_bufmgr_fake *)bo->bufmgr;
drm_intel_bo_fake *bo_fake = (drm_intel_bo_fake *)bo;
assert(!bo_fake->backing_store);
assert(!(bo_fake->flags & (BM_PINNED|BM_NO_BACKING_STORE)));
bo_fake->backing_store = malloc(bo->size);
DBG("alloc_backing - buf %d %p %d\n", bo_fake->id, bo_fake->backing_store, bo->size);
assert(bo_fake->backing_store);
}
static void
free_backing_store(drm_intel_bo *bo)
{
drm_intel_bo_fake *bo_fake = (drm_intel_bo_fake *)bo;
if (bo_fake->backing_store) {
assert(!(bo_fake->flags & (BM_PINNED|BM_NO_BACKING_STORE)));
free(bo_fake->backing_store);
bo_fake->backing_store = NULL;
}
}
static void
set_dirty(drm_intel_bo *bo)
{
drm_intel_bufmgr_fake *bufmgr_fake = (drm_intel_bufmgr_fake *)bo->bufmgr;
drm_intel_bo_fake *bo_fake = (drm_intel_bo_fake *)bo;
if (bo_fake->flags & BM_NO_BACKING_STORE && bo_fake->invalidate_cb != NULL)
bo_fake->invalidate_cb(bo, bo_fake->invalidate_ptr);
assert(!(bo_fake->flags & BM_PINNED));
DBG("set_dirty - buf %d\n", bo_fake->id);
bo_fake->dirty = 1;
}
static int
evict_lru(drm_intel_bufmgr_fake *bufmgr_fake, unsigned int max_fence)
{
struct block *block, *tmp;
DBG("%s\n", __FUNCTION__);
DRMLISTFOREACHSAFE(block, tmp, &bufmgr_fake->lru) {
drm_intel_bo_fake *bo_fake = (drm_intel_bo_fake *)block->bo;
if (bo_fake != NULL && (bo_fake->flags & BM_NO_FENCE_SUBDATA))
continue;
if (block->fence && max_fence && !FENCE_LTE(block->fence, max_fence))
return 0;
set_dirty(&bo_fake->bo);
bo_fake->block = NULL;
free_block(bufmgr_fake, block);
return 1;
}
return 0;
}
static int
evict_mru(drm_intel_bufmgr_fake *bufmgr_fake)
{
struct block *block, *tmp;
DBG("%s\n", __FUNCTION__);
DRMLISTFOREACHSAFEREVERSE(block, tmp, &bufmgr_fake->lru) {
drm_intel_bo_fake *bo_fake = (drm_intel_bo_fake *)block->bo;
if (bo_fake && (bo_fake->flags & BM_NO_FENCE_SUBDATA))
continue;
set_dirty(&bo_fake->bo);
bo_fake->block = NULL;
free_block(bufmgr_fake, block);
return 1;
}
return 0;
}
/**
* Removes all objects from the fenced list older than the given fence.
*/
static int clear_fenced(drm_intel_bufmgr_fake *bufmgr_fake,
unsigned int fence_cookie)
{
struct block *block, *tmp;
int ret = 0;
bufmgr_fake->last_fence = fence_cookie;
DRMLISTFOREACHSAFE(block, tmp, &bufmgr_fake->fenced) {
assert(block->fenced);
if (_fence_test(bufmgr_fake, block->fence)) {
block->fenced = 0;
if (!block->bo) {
DBG("delayed free: offset %x sz %x\n",
block->mem->ofs, block->mem->size);
DRMLISTDEL(block);
mmFreeMem(block->mem);
free(block);
}
else {
DBG("return to lru: offset %x sz %x\n",
block->mem->ofs, block->mem->size);
DRMLISTDEL(block);
DRMLISTADDTAIL(block, &bufmgr_fake->lru);
}
ret = 1;
}
else {
/* Blocks are ordered by fence, so if one fails, all from
* here will fail also:
*/
DBG("fence not passed: offset %x sz %x %d %d \n",
block->mem->ofs, block->mem->size, block->fence, bufmgr_fake->last_fence);
break;
}
}
DBG("%s: %d\n", __FUNCTION__, ret);
return ret;
}
static void fence_blocks(drm_intel_bufmgr_fake *bufmgr_fake, unsigned fence)
{
struct block *block, *tmp;
DRMLISTFOREACHSAFE(block, tmp, &bufmgr_fake->on_hardware) {
DBG("Fence block %p (sz 0x%x ofs %x buf %p) with fence %d\n", block,
block->mem->size, block->mem->ofs, block->bo, fence);
block->fence = fence;
block->on_hardware = 0;
block->fenced = 1;
/* Move to tail of pending list here
*/
DRMLISTDEL(block);
DRMLISTADDTAIL(block, &bufmgr_fake->fenced);
}
assert(DRMLISTEMPTY(&bufmgr_fake->on_hardware));
}
static int evict_and_alloc_block(drm_intel_bo *bo)
{
drm_intel_bufmgr_fake *bufmgr_fake = (drm_intel_bufmgr_fake *)bo->bufmgr;
drm_intel_bo_fake *bo_fake = (drm_intel_bo_fake *)bo;
assert(bo_fake->block == NULL);
/* Search for already free memory:
*/
if (alloc_block(bo))
return 1;
/* If we're not thrashing, allow lru eviction to dig deeper into
* recently used textures. We'll probably be thrashing soon:
*/
if (!bufmgr_fake->thrashing) {
while (evict_lru(bufmgr_fake, 0))
if (alloc_block(bo))
return 1;
}
/* Keep thrashing counter alive?
*/
if (bufmgr_fake->thrashing)
bufmgr_fake->thrashing = 20;
/* Wait on any already pending fences - here we are waiting for any
* freed memory that has been submitted to hardware and fenced to
* become available:
*/
while (!DRMLISTEMPTY(&bufmgr_fake->fenced)) {
uint32_t fence = bufmgr_fake->fenced.next->fence;
_fence_wait_internal(bufmgr_fake, fence);
if (alloc_block(bo))
return 1;
}
if (!DRMLISTEMPTY(&bufmgr_fake->on_hardware)) {
while (!DRMLISTEMPTY(&bufmgr_fake->fenced)) {
uint32_t fence = bufmgr_fake->fenced.next->fence;
_fence_wait_internal(bufmgr_fake, fence);
}
if (!bufmgr_fake->thrashing) {
DBG("thrashing\n");
}
bufmgr_fake->thrashing = 20;
if (alloc_block(bo))
return 1;
}
while (evict_mru(bufmgr_fake))
if (alloc_block(bo))
return 1;
DBG("%s 0x%x bytes failed\n", __FUNCTION__, bo->size);
return 0;
}
/***********************************************************************
* Public functions
*/
/**
* Wait for hardware idle by emitting a fence and waiting for it.
*/
static void
drm_intel_bufmgr_fake_wait_idle(drm_intel_bufmgr_fake *bufmgr_fake)
{
unsigned int cookie;
cookie = _fence_emit_internal(bufmgr_fake);
_fence_wait_internal(bufmgr_fake, cookie);
}
/**
* Wait for rendering to a buffer to complete.
*
* It is assumed that the bathcbuffer which performed the rendering included
* the necessary flushing.
*/
static void
drm_intel_fake_bo_wait_rendering_locked(drm_intel_bo *bo)
{
drm_intel_bufmgr_fake *bufmgr_fake = (drm_intel_bufmgr_fake *)bo->bufmgr;
drm_intel_bo_fake *bo_fake = (drm_intel_bo_fake *)bo;
if (bo_fake->block == NULL || !bo_fake->block->fenced)
return;
_fence_wait_internal(bufmgr_fake, bo_fake->block->fence);
}
static void
drm_intel_fake_bo_wait_rendering(drm_intel_bo *bo)
{
drm_intel_bufmgr_fake *bufmgr_fake = (drm_intel_bufmgr_fake *)bo->bufmgr;
pthread_mutex_lock(&bufmgr_fake->lock);
drm_intel_fake_bo_wait_rendering_locked(bo);
pthread_mutex_unlock(&bufmgr_fake->lock);
}
/* Specifically ignore texture memory sharing.
* -- just evict everything
* -- and wait for idle
*/
void
drm_intel_bufmgr_fake_contended_lock_take(drm_intel_bufmgr *bufmgr)
{
drm_intel_bufmgr_fake *bufmgr_fake = (drm_intel_bufmgr_fake *)bufmgr;
struct block *block, *tmp;
pthread_mutex_lock(&bufmgr_fake->lock);
bufmgr_fake->need_fence = 1;
bufmgr_fake->fail = 0;
/* Wait for hardware idle. We don't know where acceleration has been
* happening, so we'll need to wait anyway before letting anything get
* put on the card again.
*/
drm_intel_bufmgr_fake_wait_idle(bufmgr_fake);
/* Check that we hadn't released the lock without having fenced the last
* set of buffers.
*/
assert(DRMLISTEMPTY(&bufmgr_fake->fenced));
assert(DRMLISTEMPTY(&bufmgr_fake->on_hardware));
DRMLISTFOREACHSAFE(block, tmp, &bufmgr_fake->lru) {
assert(_fence_test(bufmgr_fake, block->fence));
set_dirty(block->bo);
}
pthread_mutex_unlock(&bufmgr_fake->lock);
}
static drm_intel_bo *
drm_intel_fake_bo_alloc(drm_intel_bufmgr *bufmgr, const char *name,
unsigned long size, unsigned int alignment)
{
drm_intel_bufmgr_fake *bufmgr_fake;
drm_intel_bo_fake *bo_fake;
bufmgr_fake = (drm_intel_bufmgr_fake *)bufmgr;
assert(size != 0);
bo_fake = calloc(1, sizeof(*bo_fake));
if (!bo_fake)
return NULL;
bo_fake->bo.size = size;
bo_fake->bo.offset = -1;
bo_fake->bo.virtual = NULL;
bo_fake->bo.bufmgr = bufmgr;
bo_fake->refcount = 1;
/* Alignment must be a power of two */
assert((alignment & (alignment - 1)) == 0);
if (alignment == 0)
alignment = 1;
bo_fake->alignment = alignment;
bo_fake->id = ++bufmgr_fake->buf_nr;
bo_fake->name = name;
bo_fake->flags = 0;
bo_fake->is_static = 0;
DBG("drm_bo_alloc: (buf %d: %s, %d kb)\n", bo_fake->id, bo_fake->name,
bo_fake->bo.size / 1024);
return &bo_fake->bo;
}
drm_intel_bo *
drm_intel_bo_fake_alloc_static(drm_intel_bufmgr *bufmgr, const char *name,
unsigned long offset, unsigned long size,
void *virtual)
{
drm_intel_bufmgr_fake *bufmgr_fake;
drm_intel_bo_fake *bo_fake;
bufmgr_fake = (drm_intel_bufmgr_fake *)bufmgr;
assert(size != 0);
bo_fake = calloc(1, sizeof(*bo_fake));
if (!bo_fake)
return NULL;
bo_fake->bo.size = size;
bo_fake->bo.offset = offset;
bo_fake->bo.virtual = virtual;
bo_fake->bo.bufmgr = bufmgr;
bo_fake->refcount = 1;
bo_fake->id = ++bufmgr_fake->buf_nr;
bo_fake->name = name;
bo_fake->flags = BM_PINNED | DRM_BO_FLAG_NO_MOVE;
bo_fake->is_static = 1;
DBG("drm_bo_alloc_static: (buf %d: %s, %d kb)\n", bo_fake->id, bo_fake->name,
bo_fake->bo.size / 1024);
return &bo_fake->bo;
}
static void
drm_intel_fake_bo_reference(drm_intel_bo *bo)
{
drm_intel_bufmgr_fake *bufmgr_fake = (drm_intel_bufmgr_fake *)bo->bufmgr;
drm_intel_bo_fake *bo_fake = (drm_intel_bo_fake *)bo;
pthread_mutex_lock(&bufmgr_fake->lock);
bo_fake->refcount++;
pthread_mutex_unlock(&bufmgr_fake->lock);
}
static void
drm_intel_fake_bo_reference_locked(drm_intel_bo *bo)
{
drm_intel_bo_fake *bo_fake = (drm_intel_bo_fake *)bo;
bo_fake->refcount++;
}
static void
drm_intel_fake_bo_unreference_locked(drm_intel_bo *bo)
{
drm_intel_bufmgr_fake *bufmgr_fake = (drm_intel_bufmgr_fake *)bo->bufmgr;
drm_intel_bo_fake *bo_fake = (drm_intel_bo_fake *)bo;
int i;
if (--bo_fake->refcount == 0) {
assert(bo_fake->map_count == 0);
/* No remaining references, so free it */
if (bo_fake->block)
free_block(bufmgr_fake, bo_fake->block);
free_backing_store(bo);
for (i = 0; i < bo_fake->nr_relocs; i++)
drm_intel_fake_bo_unreference_locked(bo_fake->relocs[i].target_buf);
DBG("drm_bo_unreference: free buf %d %s\n", bo_fake->id, bo_fake->name);
free(bo_fake->relocs);
free(bo);
}
}
static void
drm_intel_fake_bo_unreference(drm_intel_bo *bo)
{
drm_intel_bufmgr_fake *bufmgr_fake = (drm_intel_bufmgr_fake *)bo->bufmgr;
pthread_mutex_lock(&bufmgr_fake->lock);
drm_intel_fake_bo_unreference_locked(bo);
pthread_mutex_unlock(&bufmgr_fake->lock);
}
/**
* Set the buffer as not requiring backing store, and instead get the callback
* invoked whenever it would be set dirty.
*/
void drm_intel_bo_fake_disable_backing_store(drm_intel_bo *bo,
void (*invalidate_cb)(drm_intel_bo *bo,
void *ptr),
void *ptr)
{
drm_intel_bufmgr_fake *bufmgr_fake = (drm_intel_bufmgr_fake *)bo->bufmgr;
drm_intel_bo_fake *bo_fake = (drm_intel_bo_fake *)bo;
pthread_mutex_lock(&bufmgr_fake->lock);
if (bo_fake->backing_store)
free_backing_store(bo);
bo_fake->flags |= BM_NO_BACKING_STORE;
DBG("disable_backing_store set buf %d dirty\n", bo_fake->id);
bo_fake->dirty = 1;
bo_fake->invalidate_cb = invalidate_cb;
bo_fake->invalidate_ptr = ptr;
/* Note that it is invalid right from the start. Also note
* invalidate_cb is called with the bufmgr locked, so cannot
* itself make bufmgr calls.
*/
if (invalidate_cb != NULL)
invalidate_cb(bo, ptr);
pthread_mutex_unlock(&bufmgr_fake->lock);
}
/**
* Map a buffer into bo->virtual, allocating either card memory space (If
* BM_NO_BACKING_STORE or BM_PINNED) or backing store, as necessary.
*/
static int
drm_intel_fake_bo_map_locked(drm_intel_bo *bo, int write_enable)
{
drm_intel_bufmgr_fake *bufmgr_fake = (drm_intel_bufmgr_fake *)bo->bufmgr;
drm_intel_bo_fake *bo_fake = (drm_intel_bo_fake *)bo;
/* Static buffers are always mapped. */
if (bo_fake->is_static) {
if (bo_fake->card_dirty) {
drm_intel_bufmgr_fake_wait_idle(bufmgr_fake);
bo_fake->card_dirty = 0;
}
return 0;
}
/* Allow recursive mapping. Mesa may recursively map buffers with
* nested display loops, and it is used internally in bufmgr_fake
* for relocation.
*/
if (bo_fake->map_count++ != 0)
return 0;
{
DBG("drm_bo_map: (buf %d: %s, %d kb)\n", bo_fake->id, bo_fake->name,
bo_fake->bo.size / 1024);
if (bo->virtual != NULL) {
drmMsg("%s: already mapped\n", __FUNCTION__);
abort();
}
else if (bo_fake->flags & (BM_NO_BACKING_STORE|BM_PINNED)) {
if (!bo_fake->block && !evict_and_alloc_block(bo)) {
DBG("%s: alloc failed\n", __FUNCTION__);
bufmgr_fake->fail = 1;
return 1;
}
else {
assert(bo_fake->block);
bo_fake->dirty = 0;
if (!(bo_fake->flags & BM_NO_FENCE_SUBDATA) &&
bo_fake->block->fenced) {
drm_intel_fake_bo_wait_rendering_locked(bo);
}
bo->virtual = bo_fake->block->virtual;
}
}
else {
if (write_enable)
set_dirty(bo);
if (bo_fake->backing_store == 0)
alloc_backing_store(bo);
if ((bo_fake->card_dirty == 1) && bo_fake->block) {
if (bo_fake->block->fenced)
drm_intel_fake_bo_wait_rendering_locked(bo);
memcpy(bo_fake->backing_store, bo_fake->block->virtual, bo_fake->block->bo->size);
bo_fake->card_dirty = 0;
}
bo->virtual = bo_fake->backing_store;
}
}
return 0;
}
static int
drm_intel_fake_bo_map(drm_intel_bo *bo, int write_enable)
{
drm_intel_bufmgr_fake *bufmgr_fake = (drm_intel_bufmgr_fake *)bo->bufmgr;
int ret;
pthread_mutex_lock(&bufmgr_fake->lock);
ret = drm_intel_fake_bo_map_locked(bo, write_enable);
pthread_mutex_unlock(&bufmgr_fake->lock);
return ret;
}
static int
drm_intel_fake_bo_unmap_locked(drm_intel_bo *bo)
{
drm_intel_bufmgr_fake *bufmgr_fake = (drm_intel_bufmgr_fake *)bo->bufmgr;
drm_intel_bo_fake *bo_fake = (drm_intel_bo_fake *)bo;
/* Static buffers are always mapped. */
if (bo_fake->is_static)
return 0;
assert(bo_fake->map_count != 0);
if (--bo_fake->map_count != 0)
return 0;
DBG("drm_bo_unmap: (buf %d: %s, %d kb)\n", bo_fake->id, bo_fake->name,
bo_fake->bo.size / 1024);
bo->virtual = NULL;
return 0;
}
static int
drm_intel_fake_bo_unmap(drm_intel_bo *bo)
{
drm_intel_bufmgr_fake *bufmgr_fake = (drm_intel_bufmgr_fake *)bo->bufmgr;
int ret;
pthread_mutex_lock(&bufmgr_fake->lock);
ret = drm_intel_fake_bo_unmap_locked(bo);
pthread_mutex_unlock(&bufmgr_fake->lock);
return ret;
}
static void
drm_intel_fake_kick_all_locked(drm_intel_bufmgr_fake *bufmgr_fake)
{
struct block *block, *tmp;
bufmgr_fake->performed_rendering = 0;
/* okay for ever BO that is on the HW kick it off.
seriously not afraid of the POLICE right now */
DRMLISTFOREACHSAFE(block, tmp, &bufmgr_fake->on_hardware) {
drm_intel_bo_fake *bo_fake = (drm_intel_bo_fake *)block->bo;
block->on_hardware = 0;
free_block(bufmgr_fake, block);
bo_fake->block = NULL;
bo_fake->validated = 0;
if (!(bo_fake->flags & BM_NO_BACKING_STORE))
bo_fake->dirty = 1;
}
}
static int
drm_intel_fake_bo_validate(drm_intel_bo *bo)
{
drm_intel_bufmgr_fake *bufmgr_fake;
drm_intel_bo_fake *bo_fake = (drm_intel_bo_fake *)bo;
bufmgr_fake = (drm_intel_bufmgr_fake *)bo->bufmgr;
DBG("drm_bo_validate: (buf %d: %s, %d kb)\n", bo_fake->id, bo_fake->name,
bo_fake->bo.size / 1024);
/* Sanity check: Buffers should be unmapped before being validated.
* This is not so much of a problem for bufmgr_fake, but TTM refuses,
* and the problem is harder to debug there.
*/
assert(bo_fake->map_count == 0);
if (bo_fake->is_static) {
/* Add it to the needs-fence list */
bufmgr_fake->need_fence = 1;
return 0;
}
/* Allocate the card memory */
if (!bo_fake->block && !evict_and_alloc_block(bo)) {
bufmgr_fake->fail = 1;
DBG("Failed to validate buf %d:%s\n", bo_fake->id, bo_fake->name);
return -1;
}
assert(bo_fake->block);
assert(bo_fake->block->bo == &bo_fake->bo);
bo->offset = bo_fake->block->mem->ofs;
/* Upload the buffer contents if necessary */
if (bo_fake->dirty) {
DBG("Upload dirty buf %d:%s, sz %d offset 0x%x\n", bo_fake->id,
bo_fake->name, bo->size, bo_fake->block->mem->ofs);
assert(!(bo_fake->flags &
(BM_NO_BACKING_STORE|BM_PINNED)));
/* Actually, should be able to just wait for a fence on the memory,
* which we would be tracking when we free it. Waiting for idle is
* a sufficiently large hammer for now.
*/
drm_intel_bufmgr_fake_wait_idle(bufmgr_fake);
/* we may never have mapped this BO so it might not have any backing
* store if this happens it should be rare, but 0 the card memory
* in any case */
if (bo_fake->backing_store)
memcpy(bo_fake->block->virtual, bo_fake->backing_store, bo->size);
else
memset(bo_fake->block->virtual, 0, bo->size);
bo_fake->dirty = 0;
}
bo_fake->block->fenced = 0;
bo_fake->block->on_hardware = 1;
DRMLISTDEL(bo_fake->block);
DRMLISTADDTAIL(bo_fake->block, &bufmgr_fake->on_hardware);
bo_fake->validated = 1;
bufmgr_fake->need_fence = 1;
return 0;
}
static void
drm_intel_fake_fence_validated(drm_intel_bufmgr *bufmgr)
{
drm_intel_bufmgr_fake *bufmgr_fake = (drm_intel_bufmgr_fake *)bufmgr;
unsigned int cookie;
cookie = _fence_emit_internal(bufmgr_fake);
fence_blocks(bufmgr_fake, cookie);
DBG("drm_fence_validated: 0x%08x cookie\n", cookie);
}
static void
drm_intel_fake_destroy(drm_intel_bufmgr *bufmgr)
{
drm_intel_bufmgr_fake *bufmgr_fake = (drm_intel_bufmgr_fake *)bufmgr;
pthread_mutex_destroy(&bufmgr_fake->lock);
mmDestroy(bufmgr_fake->heap);
free(bufmgr);
}
static int
drm_intel_fake_emit_reloc(drm_intel_bo *bo, uint32_t offset,
drm_intel_bo *target_bo, uint32_t target_offset,
uint32_t read_domains, uint32_t write_domain)
{
drm_intel_bufmgr_fake *bufmgr_fake = (drm_intel_bufmgr_fake *)bo->bufmgr;
struct fake_buffer_reloc *r;
drm_intel_bo_fake *bo_fake = (drm_intel_bo_fake *)bo;
drm_intel_bo_fake *target_fake = (drm_intel_bo_fake *)target_bo;
int i;
pthread_mutex_lock(&bufmgr_fake->lock);
assert(bo);
assert(target_bo);
if (bo_fake->relocs == NULL) {
bo_fake->relocs = malloc(sizeof(struct fake_buffer_reloc) * MAX_RELOCS);
}
r = &bo_fake->relocs[bo_fake->nr_relocs++];
assert(bo_fake->nr_relocs <= MAX_RELOCS);
drm_intel_fake_bo_reference_locked(target_bo);
if (!target_fake->is_static) {
bo_fake->child_size += ALIGN(target_bo->size, target_fake->alignment);
bo_fake->child_size += target_fake->child_size;
}
r->target_buf = target_bo;
r->offset = offset;
r->last_target_offset = target_bo->offset;
r->delta = target_offset;
r->read_domains = read_domains;
r->write_domain = write_domain;
if (bufmgr_fake->debug) {
/* Check that a conflicting relocation hasn't already been emitted. */
for (i = 0; i < bo_fake->nr_relocs - 1; i++) {
struct fake_buffer_reloc *r2 = &bo_fake->relocs[i];
assert(r->offset != r2->offset);
}
}
pthread_mutex_unlock(&bufmgr_fake->lock);
return 0;
}
/**
* Incorporates the validation flags associated with each relocation into
* the combined validation flags for the buffer on this batchbuffer submission.
*/
static void
drm_intel_fake_calculate_domains(drm_intel_bo *bo)
{
drm_intel_bo_fake *bo_fake = (drm_intel_bo_fake *)bo;
int i;
for (i = 0; i < bo_fake->nr_relocs; i++) {
struct fake_buffer_reloc *r = &bo_fake->relocs[i];
drm_intel_bo_fake *target_fake = (drm_intel_bo_fake *)r->target_buf;
/* Do the same for the tree of buffers we depend on */
drm_intel_fake_calculate_domains(r->target_buf);
target_fake->read_domains |= r->read_domains;
target_fake->write_domain |= r->write_domain;
}
}
static int
drm_intel_fake_reloc_and_validate_buffer(drm_intel_bo *bo)
{
drm_intel_bufmgr_fake *bufmgr_fake = (drm_intel_bufmgr_fake *)bo->bufmgr;
drm_intel_bo_fake *bo_fake = (drm_intel_bo_fake *)bo;
int i, ret;
assert(bo_fake->map_count == 0);
for (i = 0; i < bo_fake->nr_relocs; i++) {
struct fake_buffer_reloc *r = &bo_fake->relocs[i];
drm_intel_bo_fake *target_fake = (drm_intel_bo_fake *)r->target_buf;
uint32_t reloc_data;
/* Validate the target buffer if that hasn't been done. */
if (!target_fake->validated) {
ret = drm_intel_fake_reloc_and_validate_buffer(r->target_buf);
if (ret != 0) {
if (bo->virtual != NULL)
drm_intel_fake_bo_unmap_locked(bo);
return ret;
}
}
/* Calculate the value of the relocation entry. */
if (r->target_buf->offset != r->last_target_offset) {
reloc_data = r->target_buf->offset + r->delta;
if (bo->virtual == NULL)
drm_intel_fake_bo_map_locked(bo, 1);
*(uint32_t *)((uint8_t *)bo->virtual + r->offset) = reloc_data;
r->last_target_offset = r->target_buf->offset;
}
}
if (bo->virtual != NULL)
drm_intel_fake_bo_unmap_locked(bo);
if (bo_fake->write_domain != 0) {
if (!(bo_fake->flags & (BM_NO_BACKING_STORE|BM_PINNED))) {
if (bo_fake->backing_store == 0)
alloc_backing_store(bo);
}
bo_fake->card_dirty = 1;
bufmgr_fake->performed_rendering = 1;
}
return drm_intel_fake_bo_validate(bo);
}
static void
drm_intel_bo_fake_post_submit(drm_intel_bo *bo)
{
drm_intel_bufmgr_fake *bufmgr_fake = (drm_intel_bufmgr_fake *)bo->bufmgr;
drm_intel_bo_fake *bo_fake = (drm_intel_bo_fake *)bo;
int i;
for (i = 0; i < bo_fake->nr_relocs; i++) {
struct fake_buffer_reloc *r = &bo_fake->relocs[i];
drm_intel_bo_fake *target_fake = (drm_intel_bo_fake *)r->target_buf;
if (target_fake->validated)
drm_intel_bo_fake_post_submit(r->target_buf);
DBG("%s@0x%08x + 0x%08x -> %s@0x%08x + 0x%08x\n",
bo_fake->name, (uint32_t)bo->offset, r->offset,
target_fake->name, (uint32_t)r->target_buf->offset, r->delta);
}
assert(bo_fake->map_count == 0);
bo_fake->validated = 0;
bo_fake->read_domains = 0;
bo_fake->write_domain = 0;
}
void drm_intel_bufmgr_fake_set_exec_callback(drm_intel_bufmgr *bufmgr,
int (*exec)(drm_intel_bo *bo,
unsigned int used,
void *priv),
void *priv)
{
drm_intel_bufmgr_fake *bufmgr_fake = (drm_intel_bufmgr_fake *)bufmgr;
bufmgr_fake->exec = exec;
bufmgr_fake->exec_priv = priv;
}
static int
drm_intel_fake_bo_exec(drm_intel_bo *bo, int used,
drm_clip_rect_t *cliprects, int num_cliprects,
int DR4)
{
drm_intel_bufmgr_fake *bufmgr_fake = (drm_intel_bufmgr_fake *)bo->bufmgr;
drm_intel_bo_fake *batch_fake = (drm_intel_bo_fake *)bo;
struct drm_i915_batchbuffer batch;
int ret;
int retry_count = 0;
pthread_mutex_lock(&bufmgr_fake->lock);
bufmgr_fake->performed_rendering = 0;
drm_intel_fake_calculate_domains(bo);
batch_fake->read_domains = I915_GEM_DOMAIN_COMMAND;
/* we've ran out of RAM so blow the whole lot away and retry */
restart:
ret = drm_intel_fake_reloc_and_validate_buffer(bo);
if (bufmgr_fake->fail == 1) {
if (retry_count == 0) {
retry_count++;
drm_intel_fake_kick_all_locked(bufmgr_fake);
bufmgr_fake->fail = 0;
goto restart;
} else /* dump out the memory here */
mmDumpMemInfo(bufmgr_fake->heap);
}
assert(ret == 0);
if (bufmgr_fake->exec != NULL) {
int ret = bufmgr_fake->exec(bo, used, bufmgr_fake->exec_priv);
if (ret != 0) {
pthread_mutex_unlock(&bufmgr_fake->lock);
return ret;
}
} else {
batch.start = bo->offset;
batch.used = used;
batch.cliprects = cliprects;
batch.num_cliprects = num_cliprects;
batch.DR1 = 0;
batch.DR4 = DR4;
if (drmCommandWrite(bufmgr_fake->fd, DRM_I915_BATCHBUFFER, &batch,
sizeof(batch))) {
drmMsg("DRM_I915_BATCHBUFFER: %d\n", -errno);
pthread_mutex_unlock(&bufmgr_fake->lock);
return -errno;
}
}
drm_intel_fake_fence_validated(bo->bufmgr);
drm_intel_bo_fake_post_submit(bo);
pthread_mutex_unlock(&bufmgr_fake->lock);
return 0;
}
/**
* Return an error if the list of BOs will exceed the aperture size.
*
* This is a rough guess and likely to fail, as during the validate sequence we
* may place a buffer in an inopportune spot early on and then fail to fit
* a set smaller than the aperture.
*/
static int
drm_intel_fake_check_aperture_space(drm_intel_bo **bo_array, int count)
{
drm_intel_bufmgr_fake *bufmgr_fake = (drm_intel_bufmgr_fake *)bo_array[0]->bufmgr;
unsigned int sz = 0;
int i;
for (i = 0; i < count; i++) {
drm_intel_bo_fake *bo_fake = (drm_intel_bo_fake *)bo_array[i];
if (bo_fake == NULL)
continue;
if (!bo_fake->is_static)
sz += ALIGN(bo_array[i]->size, bo_fake->alignment);
sz += bo_fake->child_size;
}
if (sz > bufmgr_fake->size) {
DBG("check_space: overflowed bufmgr size, %dkb vs %dkb\n",
sz / 1024, bufmgr_fake->size / 1024);
return -1;
}
DBG("drm_check_space: sz %dkb vs bufgr %dkb\n", sz / 1024 ,
bufmgr_fake->size / 1024);
return 0;
}
/**
* Evicts all buffers, waiting for fences to pass and copying contents out
* as necessary.
*
* Used by the X Server on LeaveVT, when the card memory is no longer our
* own.
*/
void
drm_intel_bufmgr_fake_evict_all(drm_intel_bufmgr *bufmgr)
{
drm_intel_bufmgr_fake *bufmgr_fake = (drm_intel_bufmgr_fake *)bufmgr;
struct block *block, *tmp;
pthread_mutex_lock(&bufmgr_fake->lock);
bufmgr_fake->need_fence = 1;
bufmgr_fake->fail = 0;
/* Wait for hardware idle. We don't know where acceleration has been
* happening, so we'll need to wait anyway before letting anything get
* put on the card again.
*/
drm_intel_bufmgr_fake_wait_idle(bufmgr_fake);
/* Check that we hadn't released the lock without having fenced the last
* set of buffers.
*/
assert(DRMLISTEMPTY(&bufmgr_fake->fenced));
assert(DRMLISTEMPTY(&bufmgr_fake->on_hardware));
DRMLISTFOREACHSAFE(block, tmp, &bufmgr_fake->lru) {
/* Releases the memory, and memcpys dirty contents out if necessary. */
free_block(bufmgr_fake, block);
}
pthread_mutex_unlock(&bufmgr_fake->lock);
}
void drm_intel_bufmgr_fake_set_last_dispatch(drm_intel_bufmgr *bufmgr,
volatile unsigned int *last_dispatch)
{
drm_intel_bufmgr_fake *bufmgr_fake = (drm_intel_bufmgr_fake *)bufmgr;
bufmgr_fake->last_dispatch = (volatile int *)last_dispatch;
}
drm_intel_bufmgr *
drm_intel_bufmgr_fake_init(int fd,
unsigned long low_offset, void *low_virtual,
unsigned long size,
volatile unsigned int *last_dispatch)
{
drm_intel_bufmgr_fake *bufmgr_fake;
bufmgr_fake = calloc(1, sizeof(*bufmgr_fake));
if (pthread_mutex_init(&bufmgr_fake->lock, NULL) != 0) {
free(bufmgr_fake);
return NULL;
}
/* Initialize allocator */
DRMINITLISTHEAD(&bufmgr_fake->fenced);
DRMINITLISTHEAD(&bufmgr_fake->on_hardware);
DRMINITLISTHEAD(&bufmgr_fake->lru);
bufmgr_fake->low_offset = low_offset;
bufmgr_fake->virtual = low_virtual;
bufmgr_fake->size = size;
bufmgr_fake->heap = mmInit(low_offset, size);
/* Hook in methods */
bufmgr_fake->bufmgr.bo_alloc = drm_intel_fake_bo_alloc;
bufmgr_fake->bufmgr.bo_reference = drm_intel_fake_bo_reference;
bufmgr_fake->bufmgr.bo_unreference = drm_intel_fake_bo_unreference;
bufmgr_fake->bufmgr.bo_map = drm_intel_fake_bo_map;
bufmgr_fake->bufmgr.bo_unmap = drm_intel_fake_bo_unmap;
bufmgr_fake->bufmgr.bo_wait_rendering = drm_intel_fake_bo_wait_rendering;
bufmgr_fake->bufmgr.bo_emit_reloc = drm_intel_fake_emit_reloc;
bufmgr_fake->bufmgr.destroy = drm_intel_fake_destroy;
bufmgr_fake->bufmgr.bo_exec = drm_intel_fake_bo_exec;
bufmgr_fake->bufmgr.check_aperture_space = drm_intel_fake_check_aperture_space;
bufmgr_fake->bufmgr.debug = 0;
bufmgr_fake->fd = fd;
bufmgr_fake->last_dispatch = (volatile int *)last_dispatch;
return &bufmgr_fake->bufmgr;
}