diff options
Diffstat (limited to 'src/sna/kgem.c')
-rw-r--r-- | src/sna/kgem.c | 1775 |
1 files changed, 1775 insertions, 0 deletions
diff --git a/src/sna/kgem.c b/src/sna/kgem.c new file mode 100644 index 00000000..0dee6e55 --- /dev/null +++ b/src/sna/kgem.c @@ -0,0 +1,1775 @@ +/* + * Copyright (c) 2011 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Authors: + * Chris Wilson <chris@chris-wilson.co.uk> + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "sna.h" +#include "sna_reg.h" + +#include <unistd.h> +#include <sys/ioctl.h> +#include <sys/mman.h> +#include <time.h> +#include <errno.h> +#include <fcntl.h> + +static inline void list_move(struct list *list, struct list *head) +{ + __list_del(list->prev, list->next); + list_add(list, head); +} + +static inline void list_replace(struct list *old, + struct list *new) +{ + new->next = old->next; + new->next->prev = new; + new->prev = old->prev; + new->prev->next = new; +} + +#define list_last_entry(ptr, type, member) \ + list_entry((ptr)->prev, type, member) + +#define list_for_each(pos, head) \ + for (pos = (head)->next; pos != (head); pos = pos->next) + + +#define DBG_NO_HW 0 +#define DBG_NO_VMAP 0 +#define DBG_NO_RELAXED_FENCING 0 +#define DBG_DUMP 0 + +#if DEBUG_KGEM +#undef DBG +#define DBG(x) ErrorF x +#else +#define NDEBUG 1 +#endif + +#define PAGE_SIZE 4096 + +struct kgem_partial_bo { + struct kgem_bo base; + uint32_t used, alloc; + uint32_t need_io : 1; + uint32_t write : 1; +}; + +static struct drm_i915_gem_exec_object2 _kgem_dummy_exec; + +static int gem_set_tiling(int fd, uint32_t handle, int tiling, int stride) +{ + struct drm_i915_gem_set_tiling set_tiling; + int ret; + + do { + set_tiling.handle = handle; + set_tiling.tiling_mode = tiling; + set_tiling.stride = stride; + + ret = ioctl(fd, DRM_IOCTL_I915_GEM_SET_TILING, &set_tiling); + } while (ret == -1 && (errno == EINTR || errno == EAGAIN)); + return set_tiling.tiling_mode; +} + +static void *gem_mmap(int fd, uint32_t handle, int size, int prot) +{ + struct drm_i915_gem_mmap_gtt mmap_arg; + void *ptr; + + DBG(("%s(handle=%d, size=%d, prot=%s)\n", __FUNCTION__, + handle, size, prot & PROT_WRITE ? "read/write" : "read-only")); + + mmap_arg.handle = handle; + if (drmIoctl(fd, DRM_IOCTL_I915_GEM_MMAP_GTT, &mmap_arg)) { + assert(0); + return NULL; + } + + ptr = mmap(0, size, prot, MAP_SHARED, fd, mmap_arg.offset); + if (ptr == MAP_FAILED) { + assert(0); + ptr = NULL; + } + + return ptr; +} + +static int gem_write(int fd, uint32_t handle, + int offset, int length, + const void *src) +{ + struct drm_i915_gem_pwrite pwrite; + + DBG(("%s(handle=%d, offset=%d, len=%d)\n", __FUNCTION__, + handle, offset, length)); + + pwrite.handle = handle; + pwrite.offset = offset; + pwrite.size = length; + pwrite.data_ptr = (uintptr_t)src; + return drmIoctl(fd, DRM_IOCTL_I915_GEM_PWRITE, &pwrite); +} + +static int gem_read(int fd, uint32_t handle, const void *dst, int length) +{ + struct drm_i915_gem_pread pread; + + DBG(("%s(handle=%d, len=%d)\n", __FUNCTION__, + handle, length)); + + pread.handle = handle; + pread.offset = 0; + pread.size = length; + pread.data_ptr = (uintptr_t)dst; + return drmIoctl(fd, DRM_IOCTL_I915_GEM_PREAD, &pread); +} + +Bool kgem_bo_write(struct kgem *kgem, struct kgem_bo *bo, + const void *data, int length) +{ + if (gem_write(kgem->fd, bo->handle, 0, length, data)) + return FALSE; + + _kgem_retire(kgem); + return TRUE; +} + +static uint32_t gem_create(int fd, int size) +{ + struct drm_i915_gem_create create; + +#if DEBUG_KGEM + assert((size & (PAGE_SIZE-1)) == 0); +#endif + + create.handle = 0; + create.size = size; + (void)drmIoctl(fd, DRM_IOCTL_I915_GEM_CREATE, &create); + + return create.handle; +} + +static bool +kgem_busy(struct kgem *kgem, int handle) +{ + struct drm_i915_gem_busy busy; + + busy.handle = handle; + busy.busy = !kgem->wedged; + (void)drmIoctl(kgem->fd, DRM_IOCTL_I915_GEM_BUSY, &busy); + + return busy.busy; +} + +static bool +gem_madvise(int fd, uint32_t handle, uint32_t state) +{ + struct drm_i915_gem_madvise madv; + int ret; + + madv.handle = handle; + madv.madv = state; + madv.retained = 1; + ret = drmIoctl(fd, DRM_IOCTL_I915_GEM_MADVISE, &madv); + assert(ret == 0); + + return madv.retained; + (void)ret; +} + +static void gem_close(int fd, uint32_t handle) +{ + struct drm_gem_close close; + + close.handle = handle; + (void)drmIoctl(fd, DRM_IOCTL_GEM_CLOSE, &close); +} + +static struct kgem_bo *__kgem_bo_init(struct kgem_bo *bo, + int handle, int size) +{ + memset(bo, 0, sizeof(*bo)); + + bo->refcnt = 1; + bo->handle = handle; + bo->aperture_size = bo->size = size; + bo->reusable = true; + bo->cpu_read = true; + bo->cpu_write = true; + list_init(&bo->request); + list_init(&bo->list); + + return bo; +} + +static struct kgem_bo *__kgem_bo_alloc(int handle, int size) +{ + struct kgem_bo *bo; + + bo = malloc(sizeof(*bo)); + if (bo == NULL) + return NULL; + + return __kgem_bo_init(bo, handle, size); +} + +static struct kgem_request *__kgem_request_alloc(void) +{ + struct kgem_request *rq; + + rq = malloc(sizeof(*rq)); + assert(rq); + if (rq == NULL) + return rq; + + list_init(&rq->buffers); + + return rq; +} + +static inline unsigned long __fls(unsigned long word) +{ + asm("bsr %1,%0" + : "=r" (word) + : "rm" (word)); + return word; +} + +static struct list *inactive(struct kgem *kgem, + int size) +{ + uint32_t order = __fls(size / PAGE_SIZE); + if (order >= ARRAY_SIZE(kgem->inactive)) + order = ARRAY_SIZE(kgem->inactive)-1; + return &kgem->inactive[order]; +} + +void kgem_init(struct kgem *kgem, int fd, int gen) +{ + drm_i915_getparam_t gp; + struct drm_i915_gem_get_aperture aperture; + int i; + + kgem->fd = fd; + kgem->gen = gen; + kgem->wedged = drmCommandNone(kgem->fd, DRM_I915_GEM_THROTTLE) == -EIO; + kgem->wedged |= DBG_NO_HW; + + kgem->ring = kgem->mode = KGEM_NONE; + kgem->flush = 0; + + kgem->nbatch = 0; + kgem->nreloc = 0; + kgem->nexec = 0; + kgem->surface = ARRAY_SIZE(kgem->batch); + list_init(&kgem->partial); + list_init(&kgem->requests); + list_init(&kgem->active); + for (i = 0; i < ARRAY_SIZE(kgem->inactive); i++) + list_init(&kgem->inactive[i]); + + kgem->next_request = __kgem_request_alloc(); + + kgem->has_vmap = 0; +#if defined(USE_VMAP) && defined(I915_PARAM_HAS_VMAP) + if (!DBG_NO_VMAP) { + drm_i915_getparam_t gp; + + gp.param = I915_PARAM_HAS_VMAP; + gp.value = &i; + kgem->has_vmap = + drmIoctl(kgem->fd, DRM_IOCTL_I915_GETPARAM, &gp) == 0 && + i > 0; + } +#endif + DBG(("%s: using vmap=%d\n", __FUNCTION__, kgem->has_vmap)); + + if (gen < 40) { + kgem->has_relaxed_fencing = 0; + if (!DBG_NO_RELAXED_FENCING) { + drm_i915_getparam_t gp; + + gp.param = I915_PARAM_HAS_RELAXED_FENCING; + gp.value = &i; + if (drmIoctl(kgem->fd, DRM_IOCTL_I915_GETPARAM, &gp) == 0) { + if (gen < 33) + kgem->has_relaxed_fencing = i >= 2; + else + kgem->has_relaxed_fencing = i > 0; + } + } + } else + kgem->has_relaxed_fencing = 1; + DBG(("%s: has relaxed fencing=%d\n", __FUNCTION__, + kgem->has_relaxed_fencing)); + + aperture.aper_available_size = 64*1024*1024; + (void)drmIoctl(fd, DRM_IOCTL_I915_GEM_GET_APERTURE, &aperture); + + kgem->aperture_high = aperture.aper_available_size * 3/4; + kgem->aperture_low = aperture.aper_available_size * 1/4; + kgem->aperture = 0; + DBG(("%s: aperture low=%d, high=%d\n", __FUNCTION__, + kgem->aperture_low, kgem->aperture_high)); + + i = 8; + gp.param = I915_PARAM_NUM_FENCES_AVAIL; + gp.value = &i; + (void)drmIoctl(fd, DRM_IOCTL_I915_GETPARAM, &gp); + kgem->fence_max = i - 2; + + DBG(("%s: max fences=%d\n", __FUNCTION__, kgem->fence_max)); +} + +/* XXX hopefully a good approximation */ +static uint32_t kgem_get_unique_id(struct kgem *kgem) +{ + uint32_t id; + id = ++kgem->unique_id; + if (id == 0) + id = ++kgem->unique_id; + return id; +} + +static uint32_t kgem_surface_size(struct kgem *kgem, + uint32_t width, + uint32_t height, + uint32_t bpp, + uint32_t tiling, + uint32_t *pitch) +{ + uint32_t tile_width, tile_height; + uint32_t size; + + if (kgem->gen == 2) { + if (tiling) { + tile_width = 512; + tile_height = 16; + } else { + tile_width = 64; + tile_height = 2; + } + } else switch (tiling) { + default: + case I915_TILING_NONE: + tile_width = 64; + tile_height = 2; + break; + case I915_TILING_X: + tile_width = 512; + tile_height = 8; + break; + case I915_TILING_Y: + tile_width = 128; + tile_height = 32; + break; + } + + *pitch = ALIGN(width * bpp / 8, tile_width); + if (kgem->gen < 40 && tiling != I915_TILING_NONE) { + if (*pitch > 8192) + return 0; + for (size = tile_width; size < *pitch; size <<= 1) + ; + *pitch = size; + } + + size = *pitch * ALIGN(height, tile_height); + if (kgem->has_relaxed_fencing || tiling == I915_TILING_NONE) + return ALIGN(size, PAGE_SIZE); + + /* We need to allocate a pot fence region for a tiled buffer. */ + if (kgem->gen < 30) + tile_width = 512 * 1024; + else + tile_width = 1024 * 1024; + while (tile_width < size) + tile_width *= 2; + return tile_width; +} + +static uint32_t kgem_aligned_height(uint32_t height, uint32_t tiling) +{ + uint32_t tile_height; + + switch (tiling) { + default: + case I915_TILING_NONE: + tile_height = 2; + break; + case I915_TILING_X: + tile_height = 8; + break; + case I915_TILING_Y: + tile_height = 32; + break; + } + + return ALIGN(height, tile_height); +} + +static struct drm_i915_gem_exec_object2 * +kgem_add_handle(struct kgem *kgem, struct kgem_bo *bo) +{ + struct drm_i915_gem_exec_object2 *exec; + + assert(kgem->nexec < ARRAY_SIZE(kgem->exec)); + exec = memset(&kgem->exec[kgem->nexec++], 0, sizeof(*exec)); + exec->handle = bo->handle; + exec->offset = bo->presumed_offset; + + kgem->aperture += bo->aperture_size; + + return exec; +} + +void _kgem_add_bo(struct kgem *kgem, struct kgem_bo *bo) +{ + bo->exec = kgem_add_handle(kgem, bo); + bo->rq = kgem->next_request; + list_move(&bo->request, &kgem->next_request->buffers); + kgem->flush |= bo->flush; +} + +static uint32_t kgem_end_batch(struct kgem *kgem) +{ + kgem->batch[kgem->nbatch++] = MI_BATCH_BUFFER_END; + if (kgem->nbatch & 1) + kgem->batch[kgem->nbatch++] = MI_NOOP; + + return kgem->nbatch; +} + +static void kgem_fixup_self_relocs(struct kgem *kgem, struct kgem_bo *bo) +{ + int n; + + for (n = 0; n < kgem->nreloc; n++) { + if (kgem->reloc[n].target_handle == 0) { + kgem->reloc[n].target_handle = bo->handle; + kgem->batch[kgem->reloc[n].offset/sizeof(kgem->batch[0])] = + kgem->reloc[n].delta + bo->presumed_offset; + } + } +} + +static void __kgem_bo_destroy(struct kgem *kgem, struct kgem_bo *bo) +{ + assert(list_is_empty(&bo->list)); + assert(bo->refcnt == 0); + + bo->src_bound = bo->dst_bound = 0; + + if(!bo->reusable) + goto destroy; + + if (!bo->deleted && !bo->exec) { + if (!gem_madvise(kgem->fd, bo->handle, I915_MADV_DONTNEED)) { + kgem->need_purge = 1; + goto destroy; + } + + bo->deleted = 1; + } + + list_move(&bo->list, (bo->rq || bo->needs_flush) ? &kgem->active : inactive(kgem, bo->size)); + return; + +destroy: + if (!bo->exec) { + list_del(&bo->request); + gem_close(kgem->fd, bo->handle); + free(bo); + } +} + +static void kgem_bo_unref(struct kgem *kgem, struct kgem_bo *bo) +{ + if (--bo->refcnt == 0) + __kgem_bo_destroy(kgem, bo); +} + +void _kgem_retire(struct kgem *kgem) +{ + struct kgem_bo *bo, *next; + + list_for_each_entry_safe(bo, next, &kgem->active, list) { + if (bo->rq == NULL && !kgem_busy(kgem, bo->handle)) { + assert(bo->needs_flush); + assert(bo->deleted); + bo->needs_flush = 0; + list_move(&bo->list, inactive(kgem, bo->size)); + } + } + + while (!list_is_empty(&kgem->requests)) { + struct kgem_request *rq; + + rq = list_first_entry(&kgem->requests, + struct kgem_request, + list); + if (kgem_busy(kgem, rq->bo->handle)) + break; + + while (!list_is_empty(&rq->buffers)) { + bo = list_first_entry(&rq->buffers, + struct kgem_bo, + request); + list_del(&bo->request); + bo->rq = NULL; + bo->gpu = false; + + if (bo->refcnt == 0 && !bo->needs_flush) { + assert(bo->deleted); + if (bo->reusable) { + list_move(&bo->list, + inactive(kgem, bo->size)); + } else { + gem_close(kgem->fd, bo->handle); + free(bo); + } + } + } + + rq->bo->refcnt--; + assert(rq->bo->refcnt == 0); + if (gem_madvise(kgem->fd, rq->bo->handle, I915_MADV_DONTNEED)) { + rq->bo->deleted = 1; + list_move(&rq->bo->list, + inactive(kgem, rq->bo->size)); + } else { + kgem->need_purge = 1; + gem_close(kgem->fd, rq->bo->handle); + free(rq->bo); + } + + list_del(&rq->list); + free(rq); + } + + kgem->retire = 0; +} + +static void kgem_commit(struct kgem *kgem) +{ + struct kgem_request *rq = kgem->next_request; + struct kgem_bo *bo, *next; + + list_for_each_entry_safe(bo, next, &rq->buffers, request) { + bo->src_bound = bo->dst_bound = 0; + bo->presumed_offset = bo->exec->offset; + bo->exec = NULL; + bo->dirty = false; + bo->gpu = true; + bo->cpu_read = false; + bo->cpu_write = false; + + if (!bo->refcnt) { + if (!bo->reusable) { +destroy: + list_del(&bo->list); + list_del(&bo->request); + gem_close(kgem->fd, bo->handle); + free(bo); + continue; + } + if (!bo->deleted) { + if (!gem_madvise(kgem->fd, bo->handle, + I915_MADV_DONTNEED)) { + kgem->need_purge = 1; + goto destroy; + } + bo->deleted = 1; + } + } + } + + list_add_tail(&rq->list, &kgem->requests); + kgem->next_request = __kgem_request_alloc(); +} + +static void kgem_close_list(struct kgem *kgem, struct list *head) +{ + while (!list_is_empty(head)) { + struct kgem_bo *bo; + + bo = list_first_entry(head, struct kgem_bo, list); + gem_close(kgem->fd, bo->handle); + list_del(&bo->list); + list_del(&bo->request); + free(bo); + } +} + +static void kgem_close_inactive(struct kgem *kgem) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(kgem->inactive); i++) + kgem_close_list(kgem, &kgem->inactive[i]); +} + +static void kgem_finish_partials(struct kgem *kgem) +{ + struct kgem_partial_bo *bo, *next; + + list_for_each_entry_safe(bo, next, &kgem->partial, base.list) { + if (!bo->base.exec) + continue; + + if (bo->write && bo->need_io) { + DBG(("%s: handle=%d, uploading %d/%d\n", + __FUNCTION__, bo->base.handle, bo->used, bo->alloc)); + gem_write(kgem->fd, bo->base.handle, + 0, bo->used, bo+1); + bo->need_io = 0; + } + + list_del(&bo->base.list); + kgem_bo_unref(kgem, &bo->base); + } +} + +static void kgem_cleanup(struct kgem *kgem) +{ + while (!list_is_empty(&kgem->partial)) { + struct kgem_bo *bo; + + bo = list_first_entry(&kgem->partial, + struct kgem_bo, + list); + list_del(&bo->list); + kgem_bo_unref(kgem, bo); + } + + while (!list_is_empty(&kgem->requests)) { + struct kgem_request *rq; + + rq = list_first_entry(&kgem->requests, + struct kgem_request, + list); + while (!list_is_empty(&rq->buffers)) { + struct kgem_bo *bo; + + bo = list_first_entry(&rq->buffers, + struct kgem_bo, + request); + list_del(&bo->request); + bo->rq = NULL; + bo->gpu = false; + if (bo->refcnt == 0) { + list_del(&bo->list); + gem_close(kgem->fd, bo->handle); + free(bo); + } + } + + list_del(&rq->list); + free(rq); + } + + kgem_close_inactive(kgem); +} + +static int kgem_batch_write(struct kgem *kgem, uint32_t handle) +{ + int ret; + + /* If there is no surface data, just upload the batch */ + if (kgem->surface == ARRAY_SIZE(kgem->batch)) + return gem_write(kgem->fd, handle, + 0, sizeof(uint32_t)*kgem->nbatch, + kgem->batch); + + /* Are the batch pages conjoint with the surface pages? */ + if (kgem->surface < kgem->nbatch + PAGE_SIZE/4) + return gem_write(kgem->fd, handle, + 0, sizeof(kgem->batch), + kgem->batch); + + /* Disjoint surface/batch, upload separately */ + ret = gem_write(kgem->fd, handle, + 0, sizeof(uint32_t)*kgem->nbatch, + kgem->batch); + if (ret) + return ret; + + return gem_write(kgem->fd, handle, + sizeof(uint32_t)*kgem->surface, + sizeof(kgem->batch) - sizeof(uint32_t)*kgem->surface, + kgem->batch + kgem->surface); +} + +void _kgem_submit(struct kgem *kgem) +{ + struct kgem_request *rq; + uint32_t batch_end; + int size; + + assert(kgem->nbatch); + assert(kgem->nbatch <= KGEM_BATCH_SIZE(kgem)); + + sna_kgem_context_switch(kgem, KGEM_NONE); + + batch_end = kgem_end_batch(kgem); + sna_kgem_flush(kgem); + + DBG(("batch[%d/%d]: %d %d %d, nreloc=%d, nexec=%d, nfence=%d, aperture=%d\n", + kgem->mode, kgem->ring, batch_end, kgem->nbatch, kgem->surface, + kgem->nreloc, kgem->nexec, kgem->nfence, kgem->aperture)); + + assert(kgem->nbatch <= ARRAY_SIZE(kgem->batch)); + assert(kgem->nreloc <= ARRAY_SIZE(kgem->reloc)); + assert(kgem->nexec < ARRAY_SIZE(kgem->exec)); + assert(kgem->nfence <= kgem->fence_max); +#if DEBUG_BATCH + __kgem_batch_debug(kgem, batch_end); +#endif + + rq = kgem->next_request; + if (kgem->surface != ARRAY_SIZE(kgem->batch)) + size = sizeof(kgem->batch); + else + size = kgem->nbatch * sizeof(kgem->batch[0]); + rq->bo = kgem_create_linear(kgem, size); + if (rq->bo) { + uint32_t handle = rq->bo->handle; + int i; + + i = kgem->nexec++; + kgem->exec[i].handle = handle; + kgem->exec[i].relocation_count = kgem->nreloc; + kgem->exec[i].relocs_ptr = (uintptr_t)kgem->reloc; + kgem->exec[i].alignment = 0; + kgem->exec[i].offset = 0; + kgem->exec[i].flags = 0; + kgem->exec[i].rsvd1 = 0; + kgem->exec[i].rsvd2 = 0; + + rq->bo->exec = &kgem->exec[i]; + list_add(&rq->bo->request, &rq->buffers); + + kgem_fixup_self_relocs(kgem, rq->bo); + kgem_finish_partials(kgem); + + if (kgem_batch_write(kgem, handle) == 0) { + struct drm_i915_gem_execbuffer2 execbuf; + int ret; + + execbuf.buffers_ptr = (uintptr_t)kgem->exec; + execbuf.buffer_count = kgem->nexec; + execbuf.batch_start_offset = 0; + execbuf.batch_len = batch_end*4; + execbuf.cliprects_ptr = 0; + execbuf.num_cliprects = 0; + execbuf.DR1 = 0; + execbuf.DR4 = 0; + execbuf.flags = kgem->ring; + execbuf.rsvd1 = 0; + execbuf.rsvd2 = 0; + + if (DBG_DUMP) { + int fd = open("/tmp/i915-batchbuffers.dump", + O_WRONLY | O_CREAT | O_APPEND, + 0666); + if (fd != -1) { + ret = write(fd, kgem->batch, batch_end*4); + fd = close(fd); + } + } + + ret = drmIoctl(kgem->fd, + DRM_IOCTL_I915_GEM_EXECBUFFER2, + &execbuf); + while (ret == -1 && errno == EBUSY) { + drmCommandNone(kgem->fd, DRM_I915_GEM_THROTTLE); + ret = drmIoctl(kgem->fd, + DRM_IOCTL_I915_GEM_EXECBUFFER2, + &execbuf); + } + if (ret == -1 && errno == EIO) { + DBG(("%s: GPU hang detected\n", __FUNCTION__)); + kgem->wedged = 1; + ret = 0; + } +#if DEBUG_KGEM + if (ret < 0) { + int i; + ErrorF("batch (end=%d, size=%d) submit failed: %d\n", + batch_end, size, errno); + + i = open("/tmp/batchbuffer", O_WRONLY | O_CREAT | O_APPEND, 0666); + if (i != -1) { + ret = write(i, kgem->batch, batch_end*4); + close(i); + } + + for (i = 0; i < kgem->nexec; i++) { + struct kgem_request *rq = kgem->next_request; + struct kgem_bo *bo, *found = NULL; + + list_for_each_entry(bo, &rq->buffers, request) { + if (bo->handle == kgem->exec[i].handle) { + found = bo; + break; + } + } + ErrorF("exec[%d] = handle:%d, presumed offset: %x, size: %d, tiling %d, fenced %d, deleted %d\n", + i, + kgem->exec[i].handle, + (int)kgem->exec[i].offset, + found ? found->size : 0, + found ? found->tiling : 0, + (int)(kgem->exec[i].flags & EXEC_OBJECT_NEEDS_FENCE), + found ? found->deleted : 1); + } + for (i = 0; i < kgem->nreloc; i++) { + ErrorF("reloc[%d] = pos:%d, target:%d, delta:%d, read:%x, write:%x, offset:%x\n", + i, + (int)kgem->reloc[i].offset, + kgem->reloc[i].target_handle, + kgem->reloc[i].delta, + kgem->reloc[i].read_domains, + kgem->reloc[i].write_domain, + (int)kgem->reloc[i].presumed_offset); + } + abort(); + } +#endif + assert(ret == 0); + + if (DEBUG_FLUSH_SYNC) { + struct drm_i915_gem_set_domain set_domain; + int ret; + + set_domain.handle = handle; + set_domain.read_domains = I915_GEM_DOMAIN_GTT; + set_domain.write_domain = I915_GEM_DOMAIN_GTT; + + ret = drmIoctl(kgem->fd, DRM_IOCTL_I915_GEM_SET_DOMAIN, &set_domain); + if (ret == -1) { + DBG(("%s: sync: GPU hang detected\n", __FUNCTION__)); + kgem->wedged = 1; + } + } + } + } + + kgem_commit(kgem); + if (kgem->wedged) + kgem_cleanup(kgem); + + kgem->nfence = 0; + kgem->nexec = 0; + kgem->nreloc = 0; + kgem->aperture = 0; + kgem->nbatch = 0; + kgem->surface = ARRAY_SIZE(kgem->batch); + kgem->mode = KGEM_NONE; + kgem->flush = 0; + + kgem->retire = 1; + + sna_kgem_reset(kgem); +} + +void kgem_throttle(struct kgem *kgem) +{ + kgem->wedged |= drmCommandNone(kgem->fd, DRM_I915_GEM_THROTTLE) == -EIO; +} + +bool kgem_needs_expire(struct kgem *kgem) +{ + int i; + + if (!list_is_empty(&kgem->active)) + return true; + + for (i = 0; i < ARRAY_SIZE(kgem->inactive); i++) { + if (!list_is_empty(&kgem->inactive[i])) + return true; + } + + return false; +} + +bool kgem_expire_cache(struct kgem *kgem) +{ + time_t now, expire; + struct kgem_bo *bo; + unsigned int size = 0, count = 0; + bool idle; + int i; + + _kgem_retire(kgem); + if (kgem->wedged) + kgem_cleanup(kgem); + + time(&now); + expire = 0; + + idle = true; + for (i = 0; i < ARRAY_SIZE(kgem->inactive); i++) { + idle &= list_is_empty(&kgem->inactive[i]); + list_for_each_entry(bo, &kgem->inactive[i], list) { + assert(bo->deleted); + if (bo->delta) { + expire = now - 5; + break; + } + + bo->delta = now; + } + } + if (!kgem->need_purge) { + if (idle) + return false; + if (expire == 0) + return true; + } + + idle = true; + for (i = 0; i < ARRAY_SIZE(kgem->inactive); i++) { + while (!list_is_empty(&kgem->inactive[i])) { + bo = list_last_entry(&kgem->inactive[i], + struct kgem_bo, list); + + if (!gem_madvise(kgem->fd, bo->handle, + I915_MADV_DONTNEED)) { + if (bo->delta > expire) { + idle = false; + break; + } + } + + count++; + size += bo->size; + + gem_close(kgem->fd, bo->handle); + list_del(&bo->list); + free(bo); + } + } + + DBG(("%s: purge? %d -- expired %d objects, %d bytes\n", __FUNCTION__, kgem->need_purge, count, size)); + + kgem->need_purge = false; + return idle; + (void)count; + (void)size; +} + +static struct kgem_bo * +search_linear_cache(struct kgem *kgem, int size, bool active) +{ + struct kgem_bo *bo, *next; + struct list *cache; + + if (!active) { + cache = inactive(kgem, size); + kgem_retire(kgem); + } else + cache = &kgem->active; + + list_for_each_entry_safe(bo, next, cache, list) { + if (size > bo->size) + continue; + + if (active && bo->tiling != I915_TILING_NONE) + continue; + + list_del(&bo->list); + + if (bo->deleted) { + if (!gem_madvise(kgem->fd, bo->handle, + I915_MADV_WILLNEED)) { + kgem->need_purge = 1; + goto next_bo; + } + + bo->deleted = 0; + } + + if (I915_TILING_NONE != bo->tiling && + gem_set_tiling(kgem->fd, bo->handle, + I915_TILING_NONE, 0) != I915_TILING_NONE) + goto next_bo; + + bo->tiling = I915_TILING_NONE; + bo->pitch = 0; + bo->delta = 0; + bo->aperture_size = bo->size; + DBG((" %s: found handle=%d (size=%d) in linear %s cache\n", + __FUNCTION__, bo->handle, bo->size, + active ? "active" : "inactive")); + assert(bo->refcnt == 0); + assert(bo->reusable); + return bo; +next_bo: + list_del(&bo->request); + gem_close(kgem->fd, bo->handle); + free(bo); + } + + return NULL; +} + +struct kgem_bo *kgem_create_for_name(struct kgem *kgem, uint32_t name) +{ + struct drm_gem_open open_arg; + + DBG(("%s(name=%d)\n", __FUNCTION__, name)); + + memset(&open_arg, 0, sizeof(open_arg)); + open_arg.name = name; + if (drmIoctl(kgem->fd, DRM_IOCTL_GEM_OPEN, &open_arg)) + return NULL; + + DBG(("%s: new handle=%d\n", __FUNCTION__, open_arg.handle)); + return __kgem_bo_alloc(open_arg.handle, 0); +} + +struct kgem_bo *kgem_create_linear(struct kgem *kgem, int size) +{ + struct kgem_bo *bo; + uint32_t handle; + + DBG(("%s(%d)\n", __FUNCTION__, size)); + + size = ALIGN(size, PAGE_SIZE); + bo = search_linear_cache(kgem, size, false); + if (bo) + return kgem_bo_reference(bo); + + handle = gem_create(kgem->fd, size); + if (handle == 0) + return NULL; + + DBG(("%s: new handle=%d\n", __FUNCTION__, handle)); + return __kgem_bo_alloc(handle, size); +} + +int kgem_choose_tiling(struct kgem *kgem, int tiling, int width, int height, int bpp) +{ + if (kgem->gen < 40) { + if (tiling) { + if (width * bpp > 8192 * 8) { + DBG(("%s: pitch too large for tliing [%d]\n", + __FUNCTION__, width*bpp/8)); + return I915_TILING_NONE; + } + + if (width > 2048 || height > 2048) { + DBG(("%s: large buffer (%dx%d), forcing TILING_X\n", + __FUNCTION__, width, height)); + return -I915_TILING_X; + } + } + } else { + if (width*bpp > (MAXSHORT-512) * 8) { + DBG(("%s: large pitch [%d], forcing TILING_X\n", + __FUNCTION__, width*bpp/8)); + return -I915_TILING_X; + } + + if (tiling && (width > 8192 || height > 8192)) { + DBG(("%s: large tiled buffer [%dx%d], forcing TILING_X\n", + __FUNCTION__, width, height)); + return -I915_TILING_X; + } + } + + if (tiling == I915_TILING_Y && height < 16) { + DBG(("%s: too short [%d] for TILING_Y\n", + __FUNCTION__,height)); + tiling = I915_TILING_X; + } + if (tiling == I915_TILING_X && height < 4) { + DBG(("%s: too short [%d] for TILING_X\n", + __FUNCTION__, height)); + tiling = I915_TILING_NONE; + } + + if (tiling == I915_TILING_X && width * bpp < 512/2) { + DBG(("%s: too thin [%d] for TILING_X\n", + __FUNCTION__, width)); + tiling = I915_TILING_NONE; + } + if (tiling == I915_TILING_Y && width * bpp < 32/2) { + DBG(("%s: too thin [%d] for TILING_Y\n", + __FUNCTION__, width)); + tiling = I915_TILING_NONE; + } + + DBG(("%s: %dx%d -> %d\n", __FUNCTION__, width, height, tiling)); + return tiling; +} + +static bool _kgem_can_create_2d(struct kgem *kgem, + int width, int height, int bpp, int tiling) +{ + uint32_t pitch, size; + + if (bpp < 8) + return false; + + size = kgem_surface_size(kgem, width, height, bpp, tiling, &pitch); + if (size == 0 || size > kgem->aperture_low) + size = kgem_surface_size(kgem, width, height, bpp, I915_TILING_NONE, &pitch); + return size > 0 && size <= kgem->aperture_low; +} + +#if DEBUG_KGEM +bool kgem_can_create_2d(struct kgem *kgem, + int width, int height, int bpp, int tiling) +{ + bool ret = _kgem_can_create_2d(kgem, width, height, bpp, tiling); + DBG(("%s(%dx%d, bpp=%d, tiling=%d) = %d\n", __FUNCTION__, + width, height, bpp, tiling, ret)); + return ret; +} +#else +bool kgem_can_create_2d(struct kgem *kgem, + int width, int height, int bpp, int tiling) +{ + return _kgem_can_create_2d(kgem, width, height, bpp, tiling); +} +#endif + +static int kgem_bo_aperture_size(struct kgem *kgem, struct kgem_bo *bo) +{ + int size; + + if (kgem->gen >= 40 || bo->tiling == I915_TILING_NONE) { + size = bo->size; + } else { + if (kgem->gen < 30) + size = 512 * 1024; + else + size = 1024 * 1024; + while (size < bo->size) + size *= 2; + } + return size; +} + +struct kgem_bo *kgem_create_2d(struct kgem *kgem, + int width, + int height, + int bpp, + int tiling, + uint32_t flags) +{ + struct list *cache; + struct kgem_bo *bo, *next; + uint32_t pitch, tiled_height[3], size; + uint32_t handle; + int exact = flags & CREATE_EXACT; + int search; + int i; + + if (tiling < 0) + tiling = -tiling, exact = 1; + + DBG(("%s(%dx%d, bpp=%d, tiling=%d, exact=%d, inactive=%d)\n", __FUNCTION__, + width, height, bpp, tiling, !!exact, !!(flags & CREATE_INACTIVE))); + + assert(_kgem_can_create_2d(kgem, width, height, bpp, tiling)); + size = kgem_surface_size(kgem, width, height, bpp, tiling, &pitch); + assert(size && size <= kgem->aperture_low); + if (flags & CREATE_INACTIVE) + goto skip_active_search; + + for (i = 0; i <= I915_TILING_Y; i++) + tiled_height[i] = kgem_aligned_height(height, i); + + search = 0; + /* Best active match first */ + list_for_each_entry_safe(bo, next, &kgem->active, list) { + uint32_t s; + + search++; + + if (exact) { + if (bo->tiling != tiling) + continue; + } else { + if (bo->tiling > tiling) + continue; + } + + if (bo->tiling) { + if (bo->pitch < pitch) { + DBG(("tiled and pitch too small: tiling=%d, (want %d), pitch=%d, need %d\n", + bo->tiling, tiling, + bo->pitch, pitch)); + continue; + } + } else + bo->pitch = pitch; + + s = bo->pitch * tiled_height[bo->tiling]; + if (s > bo->size) { + DBG(("size too small: %d < %d\n", + bo->size, s)); + continue; + } + + list_del(&bo->list); + + if (bo->deleted) { + if (!gem_madvise(kgem->fd, bo->handle, + I915_MADV_WILLNEED)) { + kgem->need_purge = 1; + gem_close(kgem->fd, bo->handle); + list_del(&bo->request); + free(bo); + continue; + } + + bo->deleted = 0; + } + + bo->unique_id = kgem_get_unique_id(kgem); + bo->delta = 0; + bo->aperture_size = kgem_bo_aperture_size(kgem, bo); + DBG((" from active: pitch=%d, tiling=%d, handle=%d, id=%d\n", + bo->pitch, bo->tiling, bo->handle, bo->unique_id)); + assert(bo->refcnt == 0); + assert(bo->reusable); + return kgem_bo_reference(bo); + } + + DBG(("searched %d active, no match\n", search)); + +skip_active_search: + /* Now just look for a close match and prefer any currently active */ + cache = inactive(kgem, size); + list_for_each_entry_safe(bo, next, cache, list) { + if (size > bo->size) { + DBG(("inactive too small: %d < %d\n", + bo->size, size)); + continue; + } + + if (bo->tiling != tiling || + (tiling != I915_TILING_NONE && bo->pitch != pitch)) { + if (tiling != gem_set_tiling(kgem->fd, + bo->handle, + tiling, pitch)) + goto next_bo; + } + + bo->pitch = pitch; + bo->tiling = tiling; + + list_del(&bo->list); + + if (bo->deleted) { + if (!gem_madvise(kgem->fd, bo->handle, + I915_MADV_WILLNEED)) { + kgem->need_purge = 1; + goto next_bo; + } + + bo->deleted = 0; + } + + bo->delta = 0; + bo->unique_id = kgem_get_unique_id(kgem); + bo->aperture_size = kgem_bo_aperture_size(kgem, bo); + assert(bo->pitch); + DBG((" from inactive: pitch=%d, tiling=%d: handle=%d, id=%d\n", + bo->pitch, bo->tiling, bo->handle, bo->unique_id)); + assert(bo->refcnt == 0); + assert(bo->reusable); + return kgem_bo_reference(bo); + +next_bo: + gem_close(kgem->fd, bo->handle); + list_del(&bo->request); + free(bo); + continue; + } + + handle = gem_create(kgem->fd, size); + if (handle == 0) + return NULL; + + bo = __kgem_bo_alloc(handle, size); + if (!bo) { + gem_close(kgem->fd, handle); + return NULL; + } + + bo->unique_id = kgem_get_unique_id(kgem); + bo->pitch = pitch; + if (tiling != I915_TILING_NONE) + bo->tiling = gem_set_tiling(kgem->fd, handle, tiling, pitch); + bo->aperture_size = kgem_bo_aperture_size(kgem, bo); + + DBG((" new pitch=%d, tiling=%d, handle=%d, id=%d\n", + bo->pitch, bo->tiling, bo->handle, bo->unique_id)); + return bo; +} + +void _kgem_bo_destroy(struct kgem *kgem, struct kgem_bo *bo) +{ + if (bo->proxy) { + kgem_bo_unref(kgem, bo->proxy); + list_del(&bo->request); + free(bo); + return; + } + + __kgem_bo_destroy(kgem, bo); +} + +void __kgem_flush(struct kgem *kgem, struct kgem_bo *bo) +{ + /* The kernel will emit a flush *and* update its own flushing lists. */ + kgem_busy(kgem, bo->handle); +} + +bool kgem_check_bo(struct kgem *kgem, struct kgem_bo *bo) +{ + if (bo == NULL) + return true; + + if (bo->exec) + return true; + + if (kgem->aperture > kgem->aperture_low) + return false; + + if (bo->size + kgem->aperture > kgem->aperture_high) + return false; + + if (kgem->nexec == KGEM_EXEC_SIZE(kgem)) + return false; + + return true; +} + +bool kgem_check_bo_fenced(struct kgem *kgem, ...) +{ + va_list ap; + struct kgem_bo *bo; + int num_fence = 0; + int num_exec = 0; + int size = 0; + + if (kgem->aperture > kgem->aperture_low) + return false; + + va_start(ap, kgem); + while ((bo = va_arg(ap, struct kgem_bo *))) { + if (bo->exec) { + if (kgem->gen >= 40 || bo->tiling == I915_TILING_NONE) + continue; + + if ((bo->exec->flags & EXEC_OBJECT_NEEDS_FENCE) == 0) + num_fence++; + + continue; + } + + size += bo->size; + num_exec++; + if (kgem->gen < 40 && bo->tiling) + num_fence++; + } + va_end(ap); + + if (size + kgem->aperture > kgem->aperture_high) + return false; + + if (kgem->nexec + num_exec >= KGEM_EXEC_SIZE(kgem)) + return false; + + if (kgem->nfence + num_fence >= kgem->fence_max) + return false; + + return true; +} + +uint32_t kgem_add_reloc(struct kgem *kgem, + uint32_t pos, + struct kgem_bo *bo, + uint32_t read_write_domain, + uint32_t delta) +{ + int index; + + index = kgem->nreloc++; + assert(index < ARRAY_SIZE(kgem->reloc)); + kgem->reloc[index].offset = pos * sizeof(kgem->batch[0]); + if (bo) { + assert(!bo->deleted); + + delta += bo->delta; + if (bo->proxy) { + /* need to release the cache upon batch submit */ + list_move(&bo->request, &kgem->next_request->buffers); + bo->exec = &_kgem_dummy_exec; + bo = bo->proxy; + } + + assert(!bo->deleted); + + if (bo->exec == NULL) { + _kgem_add_bo(kgem, bo); + if (bo->needs_flush && + (read_write_domain >> 16) != I915_GEM_DOMAIN_RENDER) + bo->needs_flush = false; + } + + if (read_write_domain & KGEM_RELOC_FENCED && kgem->gen < 40) { + if (bo->tiling && + (bo->exec->flags & EXEC_OBJECT_NEEDS_FENCE) == 0) { + assert(kgem->nfence < kgem->fence_max); + kgem->nfence++; + } + bo->exec->flags |= EXEC_OBJECT_NEEDS_FENCE; + } + + kgem->reloc[index].delta = delta; + kgem->reloc[index].target_handle = bo->handle; + kgem->reloc[index].presumed_offset = bo->presumed_offset; + + if (read_write_domain & 0x7fff) + bo->needs_flush = bo->dirty = true; + + delta += bo->presumed_offset; + } else { + kgem->reloc[index].delta = delta; + kgem->reloc[index].target_handle = 0; + kgem->reloc[index].presumed_offset = 0; + } + kgem->reloc[index].read_domains = read_write_domain >> 16; + kgem->reloc[index].write_domain = read_write_domain & 0x7fff; + + return delta; +} + +void *kgem_bo_map(struct kgem *kgem, struct kgem_bo *bo, int prot) +{ + return gem_mmap(kgem->fd, bo->handle, bo->size, prot); +} + +uint32_t kgem_bo_flink(struct kgem *kgem, struct kgem_bo *bo) +{ + struct drm_gem_flink flink; + int ret; + + memset(&flink, 0, sizeof(flink)); + flink.handle = bo->handle; + ret = drmIoctl(kgem->fd, DRM_IOCTL_GEM_FLINK, &flink); + if (ret) + return 0; + + bo->reusable = false; + return flink.name; +} + +#if defined(USE_VMAP) && defined(I915_PARAM_HAS_VMAP) +static uint32_t gem_vmap(int fd, void *ptr, int size, int read_only) +{ + struct drm_i915_gem_vmap vmap; + + vmap.user_ptr = (uintptr_t)ptr; + vmap.user_size = size; + vmap.flags = 0; + if (read_only) + vmap.flags |= I915_VMAP_READ_ONLY; + + if (drmIoctl(fd, DRM_IOCTL_I915_GEM_VMAP, &vmap)) + return 0; + + return vmap.handle; +} + +struct kgem_bo *kgem_create_map(struct kgem *kgem, + void *ptr, uint32_t size, + bool read_only) +{ + struct kgem_bo *bo; + uint32_t handle; + + if (!kgem->has_vmap) + return NULL; + + handle = gem_vmap(kgem->fd, ptr, size, read_only); + if (handle == 0) + return NULL; + + bo = __kgem_bo_alloc(handle, size); + if (bo == NULL) { + gem_close(kgem->fd, handle); + return NULL; + } + + bo->reusable = false; + bo->sync = true; + DBG(("%s(ptr=%p, size=%d, read_only=%d) => handle=%d\n", + __FUNCTION__, ptr, size, read_only, handle)); + return bo; +} +#else +static uint32_t gem_vmap(int fd, void *ptr, int size, int read_only) +{ + return 0; +} + +struct kgem_bo *kgem_create_map(struct kgem *kgem, + void *ptr, uint32_t size, + bool read_only) +{ + return NULL; +} +#endif + +void kgem_bo_sync(struct kgem *kgem, struct kgem_bo *bo, bool for_write) +{ + struct drm_i915_gem_set_domain set_domain; + + kgem_bo_submit(kgem, bo); + if (for_write ? bo->cpu_write : bo->cpu_read) + return; + + set_domain.handle = bo->handle; + set_domain.read_domains = I915_GEM_DOMAIN_CPU; + set_domain.write_domain = for_write ? I915_GEM_DOMAIN_CPU : 0; + + drmIoctl(kgem->fd, DRM_IOCTL_I915_GEM_SET_DOMAIN, &set_domain); + _kgem_retire(kgem); + bo->cpu_read = true; + if (for_write) + bo->cpu_write = true; +} + +void kgem_clear_dirty(struct kgem *kgem) +{ + struct kgem_request *rq = kgem->next_request; + struct kgem_bo *bo; + + list_for_each_entry(bo, &rq->buffers, request) + bo->dirty = false; +} + +/* Flush the contents of the RenderCache and invalidate the TextureCache */ +void kgem_emit_flush(struct kgem *kgem) +{ + if (kgem->nbatch == 0) + return; + + if (!kgem_check_batch(kgem, 4)) { + _kgem_submit(kgem); + return; + } + + DBG(("%s()\n", __FUNCTION__)); + + if (kgem->ring == KGEM_BLT) { + kgem->batch[kgem->nbatch++] = MI_FLUSH_DW | 2; + kgem->batch[kgem->nbatch++] = 0; + kgem->batch[kgem->nbatch++] = 0; + kgem->batch[kgem->nbatch++] = 0; + } else if (kgem->gen >= 50 && 0) { + kgem->batch[kgem->nbatch++] = PIPE_CONTROL | 2; + kgem->batch[kgem->nbatch++] = + PIPE_CONTROL_WC_FLUSH | + PIPE_CONTROL_TC_FLUSH | + PIPE_CONTROL_NOWRITE; + kgem->batch[kgem->nbatch++] = 0; + kgem->batch[kgem->nbatch++] = 0; + } else { + if ((kgem->batch[kgem->nbatch-1] & (0xff<<23)) == MI_FLUSH) + kgem->nbatch--; + kgem->batch[kgem->nbatch++] = MI_FLUSH | MI_INVALIDATE_MAP_CACHE; + } + + kgem_clear_dirty(kgem); +} + +struct kgem_bo *kgem_create_proxy(struct kgem_bo *target, + int offset, int length) +{ + struct kgem_bo *bo; + + assert(target->proxy == NULL); + + bo = __kgem_bo_alloc(target->handle, length); + if (bo == NULL) + return NULL; + + bo->reusable = false; + bo->proxy = kgem_bo_reference(target); + bo->delta = offset; + return bo; +} + +struct kgem_bo *kgem_create_buffer(struct kgem *kgem, + uint32_t size, uint32_t flags, + void **ret) +{ + struct kgem_partial_bo *bo; + bool write = !!(flags & KGEM_BUFFER_WRITE); + int offset = 0; + + DBG(("%s: size=%d, flags=%x\n", __FUNCTION__, size, flags)); + + list_for_each_entry(bo, &kgem->partial, base.list) { + if (bo->write != write) + continue; + if (bo->used + size < bo->alloc) { + DBG(("%s: reusing partial buffer? used=%d, total=%d\n", + __FUNCTION__, bo->used, bo->alloc)); + offset = bo->used; + bo->used += size; + break; + } + } + + if (offset == 0) { + uint32_t handle; + int alloc; + + alloc = (flags & KGEM_BUFFER_LAST) ? 4096 : 32 * 1024; + alloc = ALIGN(size, alloc); + + bo = malloc(sizeof(*bo) + alloc); + if (bo == NULL) + return NULL; + + handle = 0; + if (kgem->has_vmap) + handle = gem_vmap(kgem->fd, bo+1, alloc, write); + if (handle == 0) { + struct kgem_bo *old; + + old = NULL; + if (!write) + old = search_linear_cache(kgem, alloc, true); + if (old == NULL) + old = search_linear_cache(kgem, alloc, false); + if (old) { + memcpy(&bo->base, old, sizeof(*old)); + if (old->rq) + list_replace(&old->request, + &bo->base.request); + else + list_init(&bo->base.request); + free(old); + bo->base.refcnt = 1; + } else { + if (!__kgem_bo_init(&bo->base, + gem_create(kgem->fd, alloc), + alloc)) { + free(bo); + return NULL; + } + } + bo->need_io = true; + } else { + __kgem_bo_init(&bo->base, handle, alloc); + bo->base.reusable = false; + bo->base.sync = true; + bo->need_io = 0; + } + + bo->alloc = alloc; + bo->used = size; + bo->write = write; + + list_add(&bo->base.list, &kgem->partial); + DBG(("%s(size=%d) new handle=%d\n", + __FUNCTION__, alloc, bo->base.handle)); + } + + *ret = (char *)(bo+1) + offset; + return kgem_create_proxy(&bo->base, offset, size); +} + +struct kgem_bo *kgem_upload_source_image(struct kgem *kgem, + const void *data, + int x, int y, + int width, int height, + int stride, int bpp) +{ + int dst_stride = ALIGN(width * bpp, 32) >> 3; + int size = dst_stride * height; + struct kgem_bo *bo; + void *dst; + + DBG(("%s : (%d, %d), (%d, %d), stride=%d, bpp=%d\n", + __FUNCTION__, x, y, width, height, stride, bpp)); + + bo = kgem_create_buffer(kgem, size, KGEM_BUFFER_WRITE, &dst); + if (bo == NULL) + return NULL; + + memcpy_blt(data, dst, bpp, + stride, dst_stride, + x, y, + 0, 0, + width, height); + + bo->pitch = dst_stride; + return bo; +} + +void kgem_buffer_sync(struct kgem *kgem, struct kgem_bo *_bo) +{ + struct kgem_partial_bo *bo; + + if (_bo->proxy) + _bo = _bo->proxy; + + bo = (struct kgem_partial_bo *)_bo; + + DBG(("%s(need_io=%s, sync=%d)\n", __FUNCTION__, + bo->need_io ? bo->write ? "write" : "read" : "none", + bo->base.sync)); + + if (bo->need_io) { + if (bo->write) + gem_write(kgem->fd, bo->base.handle, + 0, bo->used, bo+1); + else + gem_read(kgem->fd, bo->base.handle, bo+1, bo->used); + _kgem_retire(kgem); + bo->need_io = 0; + } + + if (bo->base.sync) + kgem_bo_sync(kgem, &bo->base, bo->write); +} |