summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--linux-core/i915_gem.c67
1 files changed, 49 insertions, 18 deletions
diff --git a/linux-core/i915_gem.c b/linux-core/i915_gem.c
index 9d701ba1..90332bf0 100644
--- a/linux-core/i915_gem.c
+++ b/linux-core/i915_gem.c
@@ -463,9 +463,9 @@ i915_gem_object_bind_to_gtt(struct drm_gem_object *obj, unsigned alignment)
DRM_INFO("Binding object of size %d at 0x%08x\n",
obj->size, obj_priv->gtt_offset);
#endif
- ret = i915_gem_object_get_page_list (obj);
+ ret = i915_gem_object_get_page_list(obj);
if (ret) {
- drm_memrange_put_block (obj_priv->gtt_space);
+ drm_memrange_put_block(obj_priv->gtt_space);
obj_priv->gtt_space = NULL;
return ret;
}
@@ -821,14 +821,43 @@ i915_dispatch_gem_execbuffer(struct drm_device *dev,
return 0;
}
+/*
+ * Kludge -- wait for almost all rendering to complete
+ * before queuing more. This uses interrupts, so the wakeup
+ * occurs without any delay.
+ */
+static int
+i915_gem_wait_space(struct drm_device *dev)
+{
+ drm_i915_private_t *dev_priv = dev->dev_private;
+ drm_i915_ring_buffer_t *ring = &(dev_priv->ring);
+ struct drm_i915_gem_object *obj_priv, *last_priv = NULL;
+ int ret = 0;
+
+ while (ring->space + 1024 < dev_priv->ring.Size &&
+ !list_empty(&dev_priv->mm.execution_list)) {
+ obj_priv = list_first_entry(&dev_priv->mm.execution_list,
+ struct drm_i915_gem_object,
+ gtt_lru_entry);
+ if (obj_priv == last_priv)
+ break;
+ ret = i915_gem_object_wait_rendering(obj_priv->obj);
+ if (ret)
+ break;
+ last_priv = obj_priv;
+ i915_kernel_lost_context(dev);
+ }
+ return ret;
+}
+
int
i915_gem_execbuffer(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
drm_i915_private_t *dev_priv = dev->dev_private;
struct drm_i915_gem_execbuffer *args = data;
- struct drm_i915_gem_exec_object *validate_list;
- struct drm_gem_object **object_list;
+ struct drm_i915_gem_exec_object *validate_list = NULL;
+ struct drm_gem_object **object_list = NULL;
struct drm_gem_object *batch_obj;
int ret, i;
uint64_t exec_offset;
@@ -842,6 +871,10 @@ i915_gem_execbuffer(struct drm_device *dev, void *data,
#endif
i915_kernel_lost_context(dev);
+ ret = i915_gem_wait_space(dev);
+ if (ret)
+ return ret;
+
/* Copy in the validate list from userland */
validate_list = drm_calloc(sizeof(*validate_list), args->buffer_count,
DRM_MEM_DRIVER);
@@ -975,7 +1008,6 @@ err:
for (i = 0; i < args->buffer_count; i++)
drm_gem_object_unreference(object_list[i]);
}
-
drm_free(object_list, sizeof(*object_list) * args->buffer_count,
DRM_MEM_DRIVER);
drm_free(validate_list, sizeof(*validate_list) * args->buffer_count,
@@ -1078,22 +1110,21 @@ i915_gem_flush_pwrite(struct drm_gem_object *obj,
{
struct drm_device *dev = obj->dev;
struct drm_i915_gem_object *obj_priv = obj->driver_private;
+
/*
- * As far as I can tell, writes of > 64 bytes will use non-temporal
- * stores which should obviate the need for this clflush.
- * It doesn't work for me though...
+ * For writes much less than the size of the object and
+ * which are already pinned in memory, do the flush right now
*/
-/* if (size <= 64) */{
- if (obj_priv->gtt_space == NULL) {
- int ret = i915_gem_object_get_page_list (obj);
- if (ret)
- return ret;
- }
- i915_gem_clflush_object(obj);
- if (obj_priv->gtt_space == NULL)
- i915_gem_object_free_page_list (obj);
+
+ if ((size < obj->size >> 1) && obj_priv->page_list != NULL) {
+ unsigned long first_page = offset / PAGE_SIZE;
+ unsigned long beyond_page = roundup(offset + size, PAGE_SIZE);
+
+ drm_ttm_cache_flush(obj_priv->page_list + first_page,
+ beyond_page - first_page);
+ drm_agp_chipset_flush(dev);
+ obj->write_domain = 0;
}
- drm_agp_chipset_flush(dev);
return 0;
}