kolibrios-gitea/drivers/video/drm/i915/sna/kgem.c

1715 lines
43 KiB
C
Raw Normal View History

/*
* 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 <drmP.h>
#include <drm.h>
#include "i915_drm.h"
#include "i915_drv.h"
#include "intel_drv.h"
#include <linux/kernel.h>
#include "../bitmap.h"
#include "sna.h"
//#include "sna_reg.h"
//#include <time.h>
//#include <errno.h>
#define NO_CACHE 1
#define list_is_empty list_empty
#define list_init INIT_LIST_HEAD
extern struct drm_device *main_device;
static struct kgem_bo *
search_linear_cache(struct kgem *kgem, unsigned int num_pages,
unsigned flags);
#define INT16_MAX (32767)
#define PAGE_ALIGN(x) ALIGN(x, PAGE_SIZE)
#define NUM_PAGES(x) (((x) + PAGE_SIZE-1) / PAGE_SIZE)
#define MAX_GTT_VMA_CACHE 512
#define MAX_CPU_VMA_CACHE INT16_MAX
#define MAP_PRESERVE_TIME 10
#define CPU_MAP(ptr) ((void*)((uintptr_t)(ptr) & ~1))
#define MAKE_CPU_MAP(ptr) ((void*)((uintptr_t)(ptr) | 1))
struct kgem_partial_bo {
struct kgem_bo base;
void *mem;
uint32_t used;
uint32_t need_io : 1;
uint32_t write : 2;
uint32_t mmapped : 1;
};
static struct kgem_bo *__kgem_freed_bo;
static struct drm_i915_gem_exec_object2 _kgem_dummy_exec;
static inline int bytes(struct kgem_bo *bo)
{
return kgem_bo_size(bo);
}
#define bucket(B) (B)->size.pages.bucket
#define num_pages(B) (B)->size.pages.count
static void kgem_sna_reset(struct kgem *kgem)
{
struct sna *sna = container_of(kgem, struct sna, kgem);
sna->render.reset(sna);
sna->blt_state.fill_bo = 0;
}
static void kgem_sna_flush(struct kgem *kgem)
{
struct sna *sna = container_of(kgem, struct sna, kgem);
sna->render.flush(sna);
if (sna->render.solid_cache.dirty)
sna_render_flush_solid(sna);
}
static int __gem_write(int fd, uint32_t handle,
int offset, int length,
const void *src)
{
DBG(("%s(handle=%x, offset=%d, len=%d)\n", __FUNCTION__,
handle, offset, length));
write_gem_object(handle, offset, length, src);
return 0;
}
static int gem_write(int fd, uint32_t handle,
int offset, int length,
const void *src)
{
u32 _offset;
u32 _size;
u8 *data_ptr;
DBG(("%s(handle=%x, offset=%d, len=%d)\n", __FUNCTION__,
handle, offset, length));
/* align the transfer to cachelines; fortuitously this is safe! */
if ((offset | length) & 63) {
_offset = offset & ~63;
_size = ALIGN(offset+length, 64) - _offset;
data_ptr = (u8*)src + _offset - offset;
} else {
_offset = offset;
_size = length;
data_ptr = (u8*)src;
}
write_gem_object(handle, _offset, _size, data_ptr);
return 0;
}
static void kgem_bo_retire(struct kgem *kgem, struct kgem_bo *bo)
{
DBG(("%s: handle=%x, domain=%d\n",
__FUNCTION__, bo->handle, bo->domain));
assert(!kgem_busy(kgem, bo->handle));
if (bo->domain == DOMAIN_GPU)
kgem_retire(kgem);
if (bo->exec == NULL) {
DBG(("%s: retiring bo handle=%x (needed flush? %d), rq? %d\n",
__FUNCTION__, bo->handle, bo->needs_flush, bo->rq != NULL));
bo->rq = NULL;
list_del(&bo->request);
bo->needs_flush = bo->flush;
}
}
Bool kgem_bo_write(struct kgem *kgem, struct kgem_bo *bo,
const void *data, int length)
{
assert(bo->refcnt);
assert(!bo->purged);
assert(!kgem_busy(kgem, bo->handle));
assert(length <= bytes(bo));
if (gem_write(kgem->fd, bo->handle, 0, length, data))
return FALSE;
DBG(("%s: flush=%d, domain=%d\n", __FUNCTION__, bo->flush, bo->domain));
kgem_bo_retire(kgem, bo);
bo->domain = DOMAIN_NONE;
return TRUE;
}
static uint32_t gem_create(int fd, int num_pages)
{
struct drm_i915_gem_object *obj;
int ret;
/* Allocate the new object */
obj = i915_gem_alloc_object(main_device,
PAGE_SIZE * num_pages);
if (obj == NULL)
goto err1;
ret = i915_gem_object_pin(obj, 4096, true);
if (ret)
goto err2;
return (uint32_t)obj;
err2:
drm_gem_object_unreference(&obj->base);
err1:
return 0;
}
static bool
kgem_bo_set_purgeable(struct kgem *kgem, struct kgem_bo *bo)
{
return true;
}
static bool
kgem_bo_clear_purgeable(struct kgem *kgem, struct kgem_bo *bo)
{
return true;
}
static void gem_close(int fd, uint32_t handle)
{
destroy_gem_object(handle);
}
/*
constant inline static unsigned long __fls(unsigned long word)
{
asm("bsr %1,%0"
: "=r" (word)
: "rm" (word));
return word;
}
*/
constant inline static int cache_bucket(int num_pages)
{
return __fls(num_pages);
}
static struct kgem_bo *__kgem_bo_init(struct kgem_bo *bo,
int handle, int num_pages)
{
assert(num_pages);
memset(bo, 0, sizeof(*bo));
bo->refcnt = 1;
bo->handle = handle;
num_pages(bo) = num_pages;
bucket(bo) = cache_bucket(num_pages);
bo->reusable = true;
bo->domain = DOMAIN_CPU;
list_init(&bo->request);
list_init(&bo->list);
list_init(&bo->vma);
return bo;
}
static struct kgem_bo *__kgem_bo_alloc(int handle, int num_pages)
{
struct kgem_bo *bo;
if (__kgem_freed_bo) {
bo = __kgem_freed_bo;
__kgem_freed_bo = *(struct kgem_bo **)bo;
} else {
bo = malloc(sizeof(*bo));
if (bo == NULL)
return NULL;
}
return __kgem_bo_init(bo, handle, num_pages);
}
static struct kgem_request _kgem_static_request;
static struct kgem_request *__kgem_request_alloc(void)
{
struct kgem_request *rq;
rq = malloc(sizeof(*rq));
if (rq == NULL)
rq = &_kgem_static_request;
list_init(&rq->buffers);
return rq;
}
static struct list_head *inactive(struct kgem *kgem, int num_pages)
{
return &kgem->inactive[cache_bucket(num_pages)];
}
static struct list_head *active(struct kgem *kgem, int num_pages, int tiling)
{
return &kgem->active[cache_bucket(num_pages)][tiling];
}
void kgem_init(struct kgem *kgem, int gen)
{
struct drm_i915_gem_get_aperture aperture;
struct drm_i915_gem_object *obj;
size_t totalram;
unsigned int i, j;
int ret;
memset(kgem, 0, sizeof(*kgem));
kgem->gen = gen;
kgem->wedged = 0;
// kgem->wedged |= DBG_NO_HW;
obj = i915_gem_alloc_object(main_device, 4096*4);
if (obj == NULL)
goto err2;
ret = i915_gem_object_pin(obj, 4096, true);
if (ret)
goto err3;
kgem->batch_ptr = drm_intel_bo_map(obj, true);
kgem->batch = kgem->batch_ptr;
kgem->batch_idx = 0;
kgem->batch_obj = obj;
kgem->max_batch_size = 1024; //ARRAY_SIZE(kgem->batch);
kgem->half_cpu_cache_pages = (2048*1024) >> 13;
list_init(&kgem->partial);
list_init(&kgem->requests);
list_init(&kgem->flushing);
list_init(&kgem->large);
for (i = 0; i < ARRAY_SIZE(kgem->inactive); i++)
list_init(&kgem->inactive[i]);
for (i = 0; i < ARRAY_SIZE(kgem->active); i++) {
for (j = 0; j < ARRAY_SIZE(kgem->active[i]); j++)
list_init(&kgem->active[i][j]);
}
for (i = 0; i < ARRAY_SIZE(kgem->vma); i++) {
for (j = 0; j < ARRAY_SIZE(kgem->vma[i].inactive); j++)
list_init(&kgem->vma[i].inactive[j]);
}
kgem->vma[MAP_GTT].count = -MAX_GTT_VMA_CACHE;
kgem->vma[MAP_CPU].count = -MAX_CPU_VMA_CACHE;
kgem->next_request = __kgem_request_alloc();
//#if defined(USE_VMAP) && defined(I915_PARAM_HAS_VMAP)
// if (!DBG_NO_VMAP)
// kgem->has_vmap = gem_param(kgem, I915_PARAM_HAS_VMAP) > 0;
//#endif
// DBG(("%s: using vmap=%d\n", __FUNCTION__, kgem->has_vmap));
if (gen < 40) {
// if (!DBG_NO_RELAXED_FENCING) {
// kgem->has_relaxed_fencing =
// gem_param(kgem, I915_PARAM_HAS_RELAXED_FENCING) > 0;
// }
} else
kgem->has_relaxed_fencing = 1;
DBG(("%s: has relaxed fencing? %d\n", __FUNCTION__,
kgem->has_relaxed_fencing));
kgem->has_llc = (gen >= 60)?true:false;
kgem->has_cpu_bo = kgem->has_llc;
DBG(("%s: cpu bo enabled %d: llc? %d\n", __FUNCTION__,
kgem->has_cpu_bo, kgem->has_llc));
kgem->has_semaphores = false;
// if (gen >= 60 && semaphores_enabled())
// kgem->has_semaphores = true;
// DBG(("%s: semaphores enabled? %d\n", __FUNCTION__,
// kgem->has_semaphores));
VG_CLEAR(aperture);
aperture.aper_size = 64*1024*1024;
i915_gem_get_aperture_ioctl(main_device, &aperture, NULL);
kgem->aperture_total = aperture.aper_size;
kgem->aperture_high = aperture.aper_size * 3/4;
kgem->aperture_low = aperture.aper_size * 1/3;
DBG(("%s: aperture low=%d [%d], high=%d [%d]\n", __FUNCTION__,
kgem->aperture_low, kgem->aperture_low / (1024*1024),
kgem->aperture_high, kgem->aperture_high / (1024*1024)));
kgem->aperture_mappable = aperture.aper_size;
DBG(("%s: aperture mappable=%d [%d MiB]\n", __FUNCTION__,
kgem->aperture_mappable, kgem->aperture_mappable / (1024*1024)));
kgem->partial_buffer_size = 64 * 1024;
while (kgem->partial_buffer_size < kgem->aperture_mappable >> 10)
kgem->partial_buffer_size *= 2;
DBG(("%s: partial buffer size=%d [%d KiB]\n", __FUNCTION__,
kgem->partial_buffer_size, kgem->partial_buffer_size / 1024));
kgem->min_alignment = 4;
if (gen < 60)
/* XXX workaround an issue where we appear to fail to
* disable dual-stream mode */
kgem->min_alignment = 64;
kgem->max_object_size = 2 * kgem->aperture_total / 3;
kgem->max_cpu_size = kgem->max_object_size;
kgem->max_gpu_size = kgem->max_object_size;
if (!kgem->has_llc)
kgem->max_gpu_size = MAX_CACHE_SIZE;
if (gen < 40) {
/* If we have to use fences for blitting, we have to make
* sure we can fit them into the aperture.
*/
kgem->max_gpu_size = kgem->aperture_mappable / 2;
if (kgem->max_gpu_size > kgem->aperture_low)
kgem->max_gpu_size = kgem->aperture_low;
}
if (kgem->max_gpu_size > kgem->max_cpu_size)
kgem->max_gpu_size = kgem->max_cpu_size;
kgem->max_upload_tile_size = kgem->aperture_mappable / 2;
if (kgem->max_upload_tile_size > kgem->max_gpu_size / 2)
kgem->max_upload_tile_size = kgem->max_gpu_size / 2;
kgem->max_copy_tile_size = (MAX_CACHE_SIZE + 1)/2;
if (kgem->max_copy_tile_size > kgem->max_gpu_size / 2)
kgem->max_copy_tile_size = kgem->max_gpu_size / 2;
totalram = 1024*1024; //total_ram_size();
if (totalram == 0) {
DBG(("%s: total ram size unknown, assuming maximum of total aperture\n",
__FUNCTION__));
totalram = kgem->aperture_total;
}
if (kgem->max_object_size > totalram / 2)
kgem->max_object_size = totalram / 2;
if (kgem->max_cpu_size > totalram / 2)
kgem->max_cpu_size = totalram / 2;
if (kgem->max_gpu_size > totalram / 4)
kgem->max_gpu_size = totalram / 4;
kgem->large_object_size = MAX_CACHE_SIZE;
if (kgem->large_object_size > kgem->max_gpu_size)
kgem->large_object_size = kgem->max_gpu_size;
DBG(("%s: large object thresold=%d\n",
__FUNCTION__, kgem->large_object_size));
DBG(("%s: max object size (gpu=%d, cpu=%d, tile upload=%d, copy=%d)\n",
__FUNCTION__,
kgem->max_gpu_size, kgem->max_cpu_size,
kgem->max_upload_tile_size, kgem->max_copy_tile_size));
/* Convert the aperture thresholds to pages */
kgem->aperture_low /= PAGE_SIZE;
kgem->aperture_high /= PAGE_SIZE;
// kgem->fence_max = gem_param(kgem, I915_PARAM_NUM_FENCES_AVAIL) - 2;
// if ((int)kgem->fence_max < 0)
kgem->fence_max = 5; /* minimum safe value for all hw */
DBG(("%s: max fences=%d\n", __FUNCTION__, kgem->fence_max));
err3:
err2:
return;
}
static struct drm_i915_gem_exec_object2 *
kgem_add_handle(struct kgem *kgem, struct kgem_bo *bo)
{
struct drm_i915_gem_exec_object2 *exec;
DBG(("%s: handle=%d, index=%d\n",
__FUNCTION__, bo->handle, kgem->nexec));
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 += num_pages(bo);
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);
/* XXX is it worth working around gcc here? */
kgem->flush |= bo->flush;
kgem->sync |= bo->sync;
kgem->scanout |= bo->scanout;
}
static uint32_t kgem_end_batch(struct kgem *kgem)
{
// kgem->context_switch(kgem, KGEM_NONE);
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->reloc[n].presumed_offset = bo->presumed_offset;
kgem->batch[kgem->reloc[n].offset/sizeof(kgem->batch[0])] =
kgem->reloc[n].delta + bo->presumed_offset;
dbgprintf("fixup reloc %d pos %d handle %d delta %x \n",
n, kgem->reloc[n].offset/sizeof(kgem->batch[0]),
bo->handle, kgem->reloc[n].delta);
}
}
}
static void kgem_bo_binding_free(struct kgem *kgem, struct kgem_bo *bo)
{
struct kgem_bo_binding *b;
b = bo->binding.next;
while (b) {
struct kgem_bo_binding *next = b->next;
free (b);
b = next;
}
}
static void kgem_bo_release_map(struct kgem *kgem, struct kgem_bo *bo)
{
int type = IS_CPU_MAP(bo->map);
DBG(("%s: releasing %s vma for handle=%d, count=%d\n",
__FUNCTION__, type ? "CPU" : "GTT",
bo->handle, kgem->vma[type].count));
VG(if (type) VALGRIND_FREELIKE_BLOCK(CPU_MAP(bo->map), 0));
// munmap(CPU_MAP(bo->map), bytes(bo));
bo->map = NULL;
if (!list_is_empty(&bo->vma)) {
list_del(&bo->vma);
kgem->vma[type].count--;
}
}
static void kgem_bo_free(struct kgem *kgem, struct kgem_bo *bo)
{
DBG(("%s: handle=%d\n", __FUNCTION__, bo->handle));
assert(bo->refcnt == 0);
assert(bo->exec == NULL);
kgem_bo_binding_free(kgem, bo);
if (bo->map)
kgem_bo_release_map(kgem, bo);
assert(list_is_empty(&bo->vma));
list_del(&bo->list);
list_del(&bo->request);
gem_close(kgem->fd, bo->handle);
if (!bo->io) {
*(struct kgem_bo **)bo = __kgem_freed_bo;
__kgem_freed_bo = bo;
} else
free(bo);
}
inline static void kgem_bo_move_to_inactive(struct kgem *kgem,
struct kgem_bo *bo)
{
assert(!kgem_busy(kgem, bo->handle));
assert(!bo->proxy);
assert(!bo->io);
assert(!bo->needs_flush);
assert(bo->rq == NULL);
assert(bo->domain != DOMAIN_GPU);
if (bucket(bo) >= NUM_CACHE_BUCKETS) {
kgem_bo_free(kgem, bo);
return;
}
list_move(&bo->list, &kgem->inactive[bucket(bo)]);
if (bo->map) {
int type = IS_CPU_MAP(bo->map);
if (bucket(bo) >= NUM_CACHE_BUCKETS ||
(!type && !kgem_bo_is_mappable(kgem, bo))) {
list_del(&bo->vma);
// munmap(CPU_MAP(bo->map), bytes(bo));
bo->map = NULL;
}
if (bo->map) {
list_move(&bo->vma, &kgem->vma[type].inactive[bucket(bo)]);
kgem->vma[type].count++;
}
}
kgem->need_expire = true;
}
inline static void kgem_bo_remove_from_inactive(struct kgem *kgem,
struct kgem_bo *bo)
{
list_del(&bo->list);
assert(bo->rq == NULL);
if (bo->map) {
assert(!list_is_empty(&bo->vma));
list_del(&bo->vma);
kgem->vma[IS_CPU_MAP(bo->map)].count--;
}
}
inline static void kgem_bo_remove_from_active(struct kgem *kgem,
struct kgem_bo *bo)
{
list_del(&bo->list);
if (bo->rq == &_kgem_static_request)
list_del(&bo->request);
assert(list_is_empty(&bo->vma));
}
static void __kgem_bo_destroy(struct kgem *kgem, struct kgem_bo *bo)
{
DBG(("%s: handle=%d\n", __FUNCTION__, bo->handle));
assert(list_is_empty(&bo->list));
assert(bo->refcnt == 0);
bo->binding.offset = 0;
if (NO_CACHE)
goto destroy;
if (bo->io) {
struct kgem_bo *base;
base = malloc(sizeof(*base));
if (base) {
DBG(("%s: transferring io handle=%d to bo\n",
__FUNCTION__, bo->handle));
/* transfer the handle to a minimum bo */
memcpy(base, bo, sizeof (*base));
base->reusable = true;
base->io = false;
list_init(&base->list);
list_replace(&bo->request, &base->request);
list_replace(&bo->vma, &base->vma);
free(bo);
bo = base;
}
}
if (!bo->reusable) {
DBG(("%s: handle=%d, not reusable\n",
__FUNCTION__, bo->handle));
goto destroy;
}
if (!kgem->has_llc && IS_CPU_MAP(bo->map) && bo->domain != DOMAIN_CPU)
kgem_bo_release_map(kgem, bo);
assert(list_is_empty(&bo->vma));
assert(list_is_empty(&bo->list));
assert(bo->vmap == false && bo->sync == false);
assert(bo->io == false);
bo->scanout = bo->flush = false;
if (bo->rq) {
struct list *cache;
DBG(("%s: handle=%d -> active\n", __FUNCTION__, bo->handle));
if (bucket(bo) < NUM_CACHE_BUCKETS)
cache = &kgem->active[bucket(bo)][bo->tiling];
else
cache = &kgem->large;
list_add(&bo->list, cache);
return;
}
assert(bo->exec == NULL);
assert(list_is_empty(&bo->request));
/*
if (bo->needs_flush) {
if ((bo->needs_flush = kgem_busy(kgem, bo->handle))) {
struct list *cache;
DBG(("%s: handle=%d -> flushing\n",
__FUNCTION__, bo->handle));
list_add(&bo->request, &kgem->flushing);
if (bucket(bo) < NUM_CACHE_BUCKETS)
cache = &kgem->active[bucket(bo)][bo->tiling];
else
cache = &kgem->large;
list_add(&bo->list, cache);
bo->rq = &_kgem_static_request;
return;
}
bo->domain = DOMAIN_NONE;
}
*/
if (!IS_CPU_MAP(bo->map)) {
if (!kgem_bo_set_purgeable(kgem, bo))
goto destroy;
if (!kgem->has_llc && bo->domain == DOMAIN_CPU)
goto destroy;
DBG(("%s: handle=%d, purged\n",
__FUNCTION__, bo->handle));
}
DBG(("%s: handle=%d -> inactive\n", __FUNCTION__, bo->handle));
kgem_bo_move_to_inactive(kgem, bo);
return;
destroy:
if (!bo->exec)
kgem_bo_free(kgem, bo);
}
bool kgem_retire(struct kgem *kgem)
{
struct kgem_bo *bo, *next;
bool retired = false;
DBG(("%s\n", __FUNCTION__));
list_for_each_entry_safe(bo, next, &kgem->flushing, request) {
assert(bo->refcnt == 0);
assert(bo->rq == &_kgem_static_request);
assert(bo->exec == NULL);
// if (kgem_busy(kgem, bo->handle))
// break;
DBG(("%s: moving %d from flush to inactive\n",
__FUNCTION__, bo->handle));
if (kgem_bo_set_purgeable(kgem, bo)) {
bo->needs_flush = false;
bo->domain = DOMAIN_NONE;
bo->rq = NULL;
list_del(&bo->request);
kgem_bo_move_to_inactive(kgem, bo);
} else
kgem_bo_free(kgem, bo);
retired = true;
}
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;
DBG(("%s: request %d complete\n",
__FUNCTION__, rq->bo->handle));
while (!list_is_empty(&rq->buffers)) {
bo = list_first_entry(&rq->buffers,
struct kgem_bo,
request);
assert(bo->rq == rq);
assert(bo->exec == NULL);
assert(bo->domain == DOMAIN_GPU);
list_del(&bo->request);
bo->rq = NULL;
// if (bo->needs_flush)
// bo->needs_flush = kgem_busy(kgem, bo->handle);
if (!bo->needs_flush)
bo->domain = DOMAIN_NONE;
if (bo->refcnt)
continue;
if (!bo->reusable) {
DBG(("%s: closing %d\n",
__FUNCTION__, bo->handle));
kgem_bo_free(kgem, bo);
continue;
}
if (bo->needs_flush) {
DBG(("%s: moving %d to flushing\n",
__FUNCTION__, bo->handle));
list_add(&bo->request, &kgem->flushing);
bo->rq = &_kgem_static_request;
} else if (kgem_bo_set_purgeable(kgem, bo)) {
DBG(("%s: moving %d to inactive\n",
__FUNCTION__, bo->handle));
kgem_bo_move_to_inactive(kgem, bo);
retired = true;
} else {
DBG(("%s: closing %d\n",
__FUNCTION__, bo->handle));
kgem_bo_free(kgem, bo);
}
}
rq->bo->refcnt--;
assert(rq->bo->refcnt == 0);
assert(rq->bo->rq == NULL);
assert(list_is_empty(&rq->bo->request));
if (kgem_bo_set_purgeable(kgem, rq->bo)) {
kgem_bo_move_to_inactive(kgem, rq->bo);
retired = true;
} else {
DBG(("%s: closing %d\n",
__FUNCTION__, rq->bo->handle));
kgem_bo_free(kgem, rq->bo);
}
list_del(&rq->list);
free(rq);
}
kgem->need_retire = !list_is_empty(&kgem->requests);
DBG(("%s -- need_retire=%d\n", __FUNCTION__, kgem->need_retire));
kgem->retire(kgem);
return retired;
}
static int kgem_batch_write(struct kgem *kgem, uint32_t handle, uint32_t size)
{
int ret;
assert(!kgem_busy(kgem, handle));
/* If there is no surface data, just upload the batch */
if (kgem->surface == kgem->max_batch_size)
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) {
assert(size == sizeof(kgem->batch));
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;
assert(kgem->nbatch*sizeof(uint32_t) <=
sizeof(uint32_t)*kgem->surface - (sizeof(kgem->batch)-size));
return __gem_write(kgem->fd, handle,
sizeof(uint32_t)*kgem->surface - (sizeof(kgem->batch)-size),
sizeof(kgem->batch) - sizeof(uint32_t)*kgem->surface,
kgem->batch + kgem->surface);
}
void kgem_reset(struct kgem *kgem)
{
// ENTER();
kgem->nfence = 0;
kgem->nexec = 0;
kgem->nreloc = 0;
kgem->aperture = 0;
kgem->aperture_fenced = 0;
kgem->nbatch = 0;
kgem->surface = kgem->max_batch_size;
kgem->mode = KGEM_NONE;
kgem->flush = 0;
kgem->scanout = 0;
kgem->batch = kgem->batch_ptr+1024*kgem->batch_idx;
kgem->next_request = __kgem_request_alloc();
kgem_sna_reset(kgem);
// dbgprintf("surface %x\n", kgem->surface);
// LEAVE();
}
static int compact_batch_surface(struct kgem *kgem)
{
int size, shrink, n;
/* See if we can pack the contents into one or two pages */
size = kgem->max_batch_size - kgem->surface + kgem->nbatch;
if (size > 2048)
return sizeof(kgem->batch);
else if (size > 1024)
size = 8192, shrink = 2*4096;
else
size = 4096, shrink = 3*4096;
for (n = 0; n < kgem->nreloc; n++) {
if (kgem->reloc[n].read_domains == I915_GEM_DOMAIN_INSTRUCTION &&
kgem->reloc[n].target_handle == 0)
kgem->reloc[n].delta -= shrink;
if (kgem->reloc[n].offset >= size)
kgem->reloc[n].offset -= shrink;
}
return size;
}
void execute_buffer (struct drm_i915_gem_object *buffer, uint32_t offset,
int size);
void _kgem_submit(struct kgem *kgem)
{
struct kgem_request *rq;
uint32_t batch_end;
int size;
assert(!DBG_NO_HW);
assert(kgem->nbatch);
assert(kgem->nbatch <= KGEM_BATCH_SIZE(kgem));
assert(kgem->nbatch <= kgem->surface);
batch_end = kgem_end_batch(kgem);
kgem_sna_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 <= kgem->max_batch_size);
assert(kgem->nbatch <= kgem->surface);
assert(kgem->nreloc <= ARRAY_SIZE(kgem->reloc));
assert(kgem->nexec < ARRAY_SIZE(kgem->exec));
assert(kgem->nfence <= kgem->fence_max);
// kgem_finish_partials(kgem);
rq = kgem->next_request;
// if (kgem->surface != kgem->max_batch_size)
// size = compact_batch_surface(kgem);
// else
size = kgem->nbatch * sizeof(kgem->batch[0]);
#if 0
{
int i;
dbgprintf("\nDump batch\n\n");
for(i=0; i < kgem->nbatch; i++)
{
dbgprintf("\t0x%08x,\t/* %d */\n",
kgem->batch[i], i);
}
dbgprintf("\ndone\n");
};
#endif
execute_buffer(kgem->batch_obj, kgem->batch_idx*4096, sizeof(uint32_t)*kgem->nbatch);
// if (kgem->wedged)
// kgem_cleanup(kgem);
kgem->batch_idx++;
kgem->batch_idx&= 3;
kgem->flush_now = kgem->scanout;
kgem_reset(kgem);
assert(kgem->next_request != NULL);
}
static struct kgem_bo *
search_linear_cache(struct kgem *kgem, unsigned int num_pages, unsigned flags)
{
struct kgem_bo *bo, *first = NULL;
bool use_active = (flags & CREATE_INACTIVE) == 0;
struct list_head *cache;
if (num_pages >= MAX_CACHE_SIZE / PAGE_SIZE)
return NULL;
if (!use_active &&
list_is_empty(inactive(kgem, num_pages)) &&
!list_is_empty(active(kgem, num_pages, I915_TILING_NONE)) &&
!kgem_retire(kgem))
return NULL;
if (!use_active && flags & (CREATE_CPU_MAP | CREATE_GTT_MAP)) {
int for_cpu = !!(flags & CREATE_CPU_MAP);
cache = &kgem->vma[for_cpu].inactive[cache_bucket(num_pages)];
list_for_each_entry(bo, cache, vma) {
assert(IS_CPU_MAP(bo->map) == for_cpu);
assert(bucket(bo) == cache_bucket(num_pages));
if (num_pages > num_pages(bo)) {
DBG(("inactive too small: %d < %d\n",
num_pages(bo), num_pages));
continue;
}
if (bo->purged && !kgem_bo_clear_purgeable(kgem, bo)) {
kgem_bo_free(kgem, bo);
break;
}
// if (I915_TILING_NONE != bo->tiling &&
// gem_set_tiling(kgem->fd, bo->handle,
// I915_TILING_NONE, 0) != I915_TILING_NONE)
// continue;
kgem_bo_remove_from_inactive(kgem, bo);
bo->tiling = I915_TILING_NONE;
bo->pitch = 0;
bo->delta = 0;
DBG((" %s: found handle=%d (num_pages=%d) in linear vma cache\n",
__FUNCTION__, bo->handle, num_pages(bo)));
assert(use_active || bo->domain != DOMAIN_GPU);
assert(!bo->needs_flush);
//assert(!kgem_busy(kgem, bo->handle));
return bo;
}
}
cache = use_active ? active(kgem, num_pages, I915_TILING_NONE) : inactive(kgem, num_pages);
list_for_each_entry(bo, cache, list) {
assert(bo->refcnt == 0);
assert(bo->reusable);
assert(!!bo->rq == !!use_active);
if (num_pages > num_pages(bo))
continue;
if (use_active && bo->tiling != I915_TILING_NONE)
continue;
if (bo->purged && !kgem_bo_clear_purgeable(kgem, bo)) {
kgem_bo_free(kgem, bo);
break;
}
/*
if (I915_TILING_NONE != bo->tiling) {
if (use_active)
continue;
if (gem_set_tiling(kgem->fd, bo->handle,
I915_TILING_NONE, 0) != I915_TILING_NONE)
continue;
bo->tiling = I915_TILING_NONE;
}
*/
if (bo->map) {
if (flags & (CREATE_CPU_MAP | CREATE_GTT_MAP)) {
int for_cpu = !!(flags & CREATE_CPU_MAP);
if (IS_CPU_MAP(bo->map) != for_cpu) {
if (first != NULL)
break;
first = bo;
continue;
}
} else {
if (first != NULL)
break;
first = bo;
continue;
}
} else {
if (flags & (CREATE_CPU_MAP | CREATE_GTT_MAP)) {
if (first != NULL)
break;
first = bo;
continue;
}
}
if (use_active)
kgem_bo_remove_from_active(kgem, bo);
else
kgem_bo_remove_from_inactive(kgem, bo);
assert(bo->tiling == I915_TILING_NONE);
bo->pitch = 0;
bo->delta = 0;
DBG((" %s: found handle=%d (num_pages=%d) in linear %s cache\n",
__FUNCTION__, bo->handle, num_pages(bo),
use_active ? "active" : "inactive"));
assert(use_active || bo->domain != DOMAIN_GPU);
assert(!bo->needs_flush || use_active);
//assert(use_active || !kgem_busy(kgem, bo->handle));
return bo;
}
if (first) {
assert(first->tiling == I915_TILING_NONE);
if (use_active)
kgem_bo_remove_from_active(kgem, first);
else
kgem_bo_remove_from_inactive(kgem, first);
first->pitch = 0;
first->delta = 0;
DBG((" %s: found handle=%d (num_pages=%d) in linear %s cache\n",
__FUNCTION__, first->handle, num_pages(first),
use_active ? "active" : "inactive"));
assert(use_active || first->domain != DOMAIN_GPU);
assert(!first->needs_flush || use_active);
//assert(use_active || !kgem_busy(kgem, first->handle));
return first;
}
return NULL;
}
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 = (size + PAGE_SIZE - 1) / PAGE_SIZE;
bo = search_linear_cache(kgem, size, CREATE_INACTIVE);
if (bo)
return kgem_bo_reference(bo);
handle = gem_create(kgem->fd, size);
if (handle == 0)
return NULL;
DBG(("%s: new handle=%x\n", __FUNCTION__, handle));
bo = __kgem_bo_alloc(handle, size);
if (bo == NULL) {
gem_close(kgem->fd, handle);
return NULL;
}
struct drm_i915_gem_object *obj;
obj = (void*)handle;
bo->gaddr = obj->gtt_offset;
return bo;
}
inline int kgem_bo_fenced_size(struct kgem *kgem, struct kgem_bo *bo)
{
unsigned int size;
assert(bo->tiling);
assert(kgem->gen < 40);
if (kgem->gen < 30)
size = 512 * 1024;
else
size = 1024 * 1024;
while (size < bytes(bo))
size *= 2;
return size;
}
void _kgem_bo_destroy(struct kgem *kgem, struct kgem_bo *bo)
{
// if (bo->proxy) {
// assert(bo->map == NULL);
// if (bo->io && bo->exec == NULL)
// _kgem_bo_delete_partial(kgem, bo);
// kgem_bo_unref(kgem, bo->proxy);
// kgem_bo_binding_free(kgem, bo);
// _list_del(&bo->request);
// free(bo);
// return;
// }
// if (bo->vmap)
// kgem_bo_sync__cpu(kgem, bo);
__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, ...)
{
va_list ap;
struct kgem_bo *bo;
int num_exec = 0;
int num_pages = 0;
va_start(ap, kgem);
while ((bo = va_arg(ap, struct kgem_bo *))) {
if (bo->exec)
continue;
if (bo->proxy) {
bo = bo->proxy;
if (bo->exec)
continue;
}
num_pages += num_pages(bo);
num_exec++;
}
va_end(ap);
if (!num_pages)
return true;
if (kgem->aperture > kgem->aperture_low)
return false;
if (num_pages + kgem->aperture > kgem->aperture_high)
return false;
if (kgem->nexec + num_exec >= 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 num_pages = 0;
int fenced_size = 0;
va_start(ap, kgem);
while ((bo = va_arg(ap, struct kgem_bo *))) {
if (bo->proxy)
bo = bo->proxy;
if (bo->exec) {
if (kgem->gen >= 40 || bo->tiling == I915_TILING_NONE)
continue;
if ((bo->exec->flags & EXEC_OBJECT_NEEDS_FENCE) == 0) {
fenced_size += kgem_bo_fenced_size(kgem, bo);
num_fence++;
}
continue;
}
num_pages += num_pages(bo);
num_exec++;
if (kgem->gen < 40 && bo->tiling) {
fenced_size += kgem_bo_fenced_size(kgem, bo);
num_fence++;
}
}
va_end(ap);
if (fenced_size + kgem->aperture_fenced > kgem->aperture_mappable)
return false;
if (kgem->nfence + num_fence > kgem->fence_max)
return false;
if (!num_pages)
return true;
if (kgem->aperture > kgem->aperture_low)
return false;
if (num_pages + kgem->aperture > kgem->aperture_high)
return false;
if (kgem->nexec + num_exec >= KGEM_EXEC_SIZE(kgem))
return false;
return true;
}
*/
#if 0
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;
DBG(("%s: handle=%d, pos=%d, delta=%d, domains=%08x\n",
__FUNCTION__, bo ? bo->handle : 0, pos, delta, read_write_domain));
assert((read_write_domain & 0x7fff) == 0 || bo != NULL);
index = kgem->nreloc++;
assert(index < ARRAY_SIZE(kgem->reloc));
kgem->reloc[index].offset = pos * sizeof(kgem->batch[0]);
if (bo) {
assert(bo->refcnt);
assert(!bo->purged);
delta += bo->delta;
if (bo->proxy) {
DBG(("%s: adding proxy for handle=%d\n",
__FUNCTION__, bo->handle));
assert(bo->handle == bo->proxy->handle);
/* 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->purged);
// if (bo->exec == NULL)
// _kgem_add_bo(kgem, bo);
// if (kgem->gen < 40 && read_write_domain & KGEM_RELOC_FENCED) {
// if (bo->tiling &&
// (bo->exec->flags & EXEC_OBJECT_NEEDS_FENCE) == 0) {
// assert(kgem->nfence < kgem->fence_max);
// kgem->aperture_fenced +=
// kgem_bo_fenced_size(kgem, bo);
// 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) {
DBG(("%s: marking handle=%d dirty\n",
__FUNCTION__, bo->handle));
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;
}
#endif
void *kgem_bo_map(struct kgem *kgem, struct kgem_bo *bo)
{
void *ptr;
DBG(("%s: handle=%d, offset=%d, tiling=%d, map=%p, domain=%d\n", __FUNCTION__,
bo->handle, bo->presumed_offset, bo->tiling, bo->map, bo->domain));
assert(!bo->purged);
assert(bo->exec == NULL);
assert(list_is_empty(&bo->list));
// if (bo->tiling == I915_TILING_NONE &&
// (kgem->has_llc || bo->domain == bo->presumed_offset)) {
DBG(("%s: converting request for GTT map into CPU map\n",
__FUNCTION__));
ptr = kgem_bo_map__cpu(kgem, bo);
// kgem_bo_sync__cpu(kgem, bo);
return ptr;
// }
#if 0
if (IS_CPU_MAP(bo->map))
kgem_bo_release_map(kgem, bo);
ptr = bo->map;
if (ptr == NULL) {
assert(bytes(bo) <= kgem->aperture_mappable / 4);
kgem_trim_vma_cache(kgem, MAP_GTT, bucket(bo));
ptr = gem_mmap(kgem->fd, bo->handle, bytes(bo),
PROT_READ | PROT_WRITE);
if (ptr == NULL)
return NULL;
/* Cache this mapping to avoid the overhead of an
* excruciatingly slow GTT pagefault. This is more an
* issue with compositing managers which need to frequently
* flush CPU damage to their GPU bo.
*/
bo->map = ptr;
DBG(("%s: caching GTT vma for %d\n", __FUNCTION__, bo->handle));
}
if (bo->domain != DOMAIN_GTT) {
struct drm_i915_gem_set_domain set_domain;
DBG(("%s: sync: needs_flush? %d, domain? %d\n", __FUNCTION__,
bo->needs_flush, bo->domain));
/* XXX use PROT_READ to avoid the write flush? */
VG_CLEAR(set_domain);
set_domain.handle = bo->handle;
set_domain.read_domains = I915_GEM_DOMAIN_GTT;
set_domain.write_domain = I915_GEM_DOMAIN_GTT;
drmIoctl(kgem->fd, DRM_IOCTL_I915_GEM_SET_DOMAIN, &set_domain);
kgem_bo_retire(kgem, bo);
bo->domain = DOMAIN_GTT;
}
#endif
return ptr;
}
void *kgem_bo_map__cpu(struct kgem *kgem, struct kgem_bo *bo)
{
// struct drm_i915_gem_mmap mmap_arg;
DBG(("%s(handle=%d, size=%d)\n", __FUNCTION__, bo->handle, bytes(bo)));
assert(!bo->purged);
assert(list_is_empty(&bo->list));
if (IS_CPU_MAP(bo->map))
return CPU_MAP(bo->map);
struct drm_i915_gem_object *obj = (void*)bo->handle;
u8 *dst;
int ret;
if(obj->pin_count == 0)
{
ret = i915_gem_object_pin(obj, 4096, true);
if (ret)
return NULL;
};
dst = drm_intel_bo_map(obj, true);
DBG(("%s: caching CPU vma for %d\n", __FUNCTION__, bo->handle));
bo->map = MAKE_CPU_MAP(dst);
return (void *)dst;
#if 0
if (bo->map)
kgem_bo_release_map(kgem, bo);
kgem_trim_vma_cache(kgem, MAP_CPU, bucket(bo));
VG_CLEAR(mmap_arg);
mmap_arg.handle = bo->handle;
mmap_arg.offset = 0;
mmap_arg.size = bytes(bo);
if (drmIoctl(kgem->fd, DRM_IOCTL_I915_GEM_MMAP, &mmap_arg)) {
ErrorF("%s: failed to mmap %d, %d bytes, into CPU domain\n",
__FUNCTION__, bo->handle, bytes(bo));
return NULL;
}
VG(VALGRIND_MALLOCLIKE_BLOCK(mmap_arg.addr_ptr, bytes(bo), 0, 1));
#endif
}
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;
}
struct kgem_bo *kgem_create_proxy(struct kgem_bo *target,
int offset, int length)
{
struct kgem_bo *bo;
DBG(("%s: target handle=%d, offset=%d, length=%d, io=%d\n",
__FUNCTION__, target->handle, offset, length, target->io));
bo = __kgem_bo_alloc(target->handle, length);
if (bo == NULL)
return NULL;
bo->reusable = false;
bo->size.bytes = length;
bo->io = target->io;
bo->dirty = target->dirty;
bo->tiling = target->tiling;
bo->pitch = target->pitch;
if (target->proxy) {
offset += target->delta;
target = target->proxy;
}
bo->proxy = kgem_bo_reference(target);
bo->delta = offset;
bo->gaddr = offset + target->gaddr;
return bo;
}
uint32_t kgem_bo_get_binding(struct kgem_bo *bo, uint32_t format)
{
struct kgem_bo_binding *b;
for (b = &bo->binding; b && b->offset; b = b->next)
if (format == b->format)
return b->offset;
return 0;
}
void kgem_bo_set_binding(struct kgem_bo *bo, uint32_t format, uint16_t offset)
{
struct kgem_bo_binding *b;
for (b = &bo->binding; b; b = b->next) {
if (b->offset)
continue;
b->offset = offset;
b->format = format;
if (b->next)
b->next->offset = 0;
return;
}
b = malloc(sizeof(*b));
if (b) {
b->next = bo->binding.next;
b->format = format;
b->offset = offset;
bo->binding.next = b;
}
}
struct kgem_bo *create_bo(bitmap_t *bitmap)
{
struct kgem_bo *bo;
bo = __kgem_bo_alloc(bitmap->obj, 1024*768*4/4096);
bo->gaddr = bitmap->gaddr;
bo->pitch = bitmap->pitch;
bo->tiling = 0;
return bo;
};