forked from KolibriOS/kolibrios
1715 lines
43 KiB
C
1715 lines
43 KiB
C
|
/*
|
||
|
* 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;
|
||
|
|
||
|
};
|