summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChris Wilson <chris@chris-wilson.co.uk>2011-12-09 15:12:03 +0000
committerChris Wilson <chris@chris-wilson.co.uk>2011-12-09 17:14:38 +0000
commitc22197f25bc0419d9f2abfcc978df5ef439feb47 (patch)
tree7d93bf2a9024f610e0e0ee66c5d8398e40ebd43a
parent429a36f7481b9bfd5ed137642d2916d69a713557 (diff)
sna: Discard bo for idle private pixmaps
If a pixmap lies around for a couple of minutes not being used, it is unlikely to be used again in the near future. Reap the GPU buffers of any of those idle pixmaps (copying to a more compact buffer in system memory) in order to free up resources for use elsewhere. Any object that is exposed via DRI is obviously exempt from this reaping. Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
-rw-r--r--src/sna/sna.h4
-rw-r--r--src/sna/sna_accel.c93
2 files changed, 92 insertions, 5 deletions
diff --git a/src/sna/sna.h b/src/sna/sna.h
index c0ac8882..bd6c8a19 100644
--- a/src/sna/sna.h
+++ b/src/sna/sna.h
@@ -135,6 +135,7 @@ struct sna_pixmap {
struct sna_damage *gpu_damage, *cpu_damage;
struct list list;
+ struct list inactive;
#define SOURCE_BIAS 4
uint16_t source_count;
@@ -212,6 +213,7 @@ enum {
enum {
FLUSH_TIMER = 0,
EXPIRE_TIMER,
+ INACTIVE_TIMER,
NUM_TIMERS
};
@@ -229,6 +231,8 @@ struct sna {
struct list deferred_free;
struct list dirty_pixmaps;
+ struct list active_pixmaps;
+ struct list inactive_clock[2];
PixmapPtr front, shadow;
PixmapPtr freed_pixmap;
diff --git a/src/sna/sna_accel.c b/src/sna/sna_accel.c
index e1905d1d..cffac538 100644
--- a/src/sna/sna_accel.c
+++ b/src/sna/sna_accel.c
@@ -171,6 +171,8 @@ static void sna_pixmap_destroy_gpu_bo(struct sna *sna, struct sna_pixmap *priv)
priv->gpu_bo = NULL;
}
+ list_del(&priv->inactive);
+
/* and reset the upload counter */
priv->source_count = SOURCE_BIAS;
}
@@ -180,6 +182,7 @@ static Bool sna_destroy_private(PixmapPtr pixmap, struct sna_pixmap *priv)
struct sna *sna = to_sna_from_pixmap(pixmap);
list_del(&priv->list);
+ list_del(&priv->inactive);
sna_damage_destroy(&priv->gpu_damage);
sna_damage_destroy(&priv->cpu_damage);
@@ -301,6 +304,7 @@ static struct sna_pixmap *_sna_pixmap_attach(PixmapPtr pixmap)
return NULL;
list_init(&priv->list);
+ list_init(&priv->inactive);
priv->pixmap = pixmap;
sna_set_pixmap(pixmap, priv);
@@ -386,6 +390,7 @@ sna_pixmap_create_scratch(ScreenPtr screen,
priv = sna_pixmap(pixmap);
memset(priv, 0, sizeof(*priv));
list_init(&priv->list);
+ list_init(&priv->inactive);
priv->pixmap = pixmap;
} else {
pixmap = create_pixmap(sna, screen, 0, 0, depth,
@@ -818,6 +823,8 @@ sna_pixmap_move_area_to_gpu(PixmapPtr pixmap, BoxPtr box)
done:
if (priv->cpu_damage == NULL)
list_del(&priv->list);
+ if (!priv->gpu_only && !priv->pinned)
+ list_move(&priv->inactive, &sna->active_pixmaps);
}
static inline Bool
@@ -993,6 +1000,7 @@ sna_pixmap_create_upload(ScreenPtr screen,
priv->pinned = 0;
priv->mapped = 0;
list_init(&priv->list);
+ list_init(&priv->inactive);
priv->pixmap = pixmap;
sna_set_pixmap(pixmap, priv);
@@ -1128,6 +1136,8 @@ done:
sna_damage_reduce_all(&priv->gpu_damage,
pixmap->drawable.width, pixmap->drawable.height);
list_del(&priv->list);
+ if (!priv->gpu_only && !priv->pinned)
+ list_move(&priv->inactive, &sna->active_pixmaps);
priv->gpu = true;
return priv;
}
@@ -8477,6 +8487,28 @@ static Bool sna_accel_do_expire(struct sna *sna)
return FALSE;
}
+static Bool sna_accel_do_inactive(struct sna *sna)
+{
+ struct itimerspec to;
+
+ return_if_timer_active(INACTIVE_TIMER);
+
+ if (list_is_empty(&sna->active_pixmaps))
+ return FALSE;
+
+ if (sna->timer[INACTIVE_TIMER] == -1)
+ return FALSE;
+
+ /* Periodic expiration after every 60s. */
+ to.it_interval.tv_sec = 60;
+ to.it_interval.tv_nsec = 0;
+ to.it_value = to.it_interval;
+ timerfd_settime(sna->timer[INACTIVE_TIMER], 0, &to, NULL);
+
+ sna->timer_active |= 1 << INACTIVE_TIMER;
+ return FALSE;
+}
+
static void sna_accel_create_timers(struct sna *sna)
{
int id;
@@ -8494,6 +8526,7 @@ static void sna_accel_create_timers(struct sna *sna)
}
static Bool sna_accel_do_flush(struct sna *sna) { return sna_accel_scanout(sna) != NULL; }
static Bool sna_accel_do_expire(struct sna *sna) { return sna->kgem.need_expire; }
+static Bool sna_accel_do_inactive(struct sna *sna) { return FALSE; }
static void sna_accel_drain_timer(struct sna *sna, int id) { }
static void _sna_accel_disarm_timer(struct sna *sna, int id) { }
#endif
@@ -8526,13 +8559,57 @@ static void sna_accel_expire(struct sna *sna)
_sna_accel_disarm_timer(sna, EXPIRE_TIMER);
}
+static void sna_accel_inactive(struct sna *sna)
+{
+ DBG(("%s (time=%ld)\n", __FUNCTION__, (long)GetTimeInMillis()));
+
+ /* clear out the oldest inactive pixmaps */
+ while (!list_is_empty(&sna->inactive_clock[1])) {
+ struct sna_pixmap *priv;
+
+ priv = list_first_entry(&sna->inactive_clock[1],
+ struct sna_pixmap,
+ inactive);
+ if (priv->pinned) {
+ list_del(&priv->inactive);
+ } else {
+ DBG(("%s: discarding GPU bo handle=%d\n",
+ __FUNCTION__, priv->gpu_bo->handle));
+ sna_pixmap_move_to_cpu(priv->pixmap, true);
+ }
+
+ /* XXX Rather than discarding the GPU buffer here, we
+ * could mark it purgeable and allow the shrinker to
+ * reap its storage only under memory pressure.
+ */
+ }
+
+ /* Age the current inactive pixmaps */
+ sna->inactive_clock[1].next = sna->inactive_clock[0].next;
+ sna->inactive_clock[0].next->prev = &sna->inactive_clock[1];
+ sna->inactive_clock[0].prev->next = &sna->inactive_clock[1];
+ sna->inactive_clock[1].prev = sna->inactive_clock[0].prev;
+
+ sna->inactive_clock[0].next = sna->active_pixmaps.next;
+ sna->active_pixmaps.next->prev = &sna->inactive_clock[0];
+ sna->active_pixmaps.prev->next = &sna->inactive_clock[0];
+ sna->inactive_clock[0].prev = sna->active_pixmaps.prev;
+
+ list_init(&sna->active_pixmaps);
+
+ if (list_is_empty(&sna->inactive_clock[1]) &&
+ list_is_empty(&sna->inactive_clock[0]))
+ _sna_accel_disarm_timer(sna, INACTIVE_TIMER);
+}
+
static void sna_accel_install_timers(struct sna *sna)
{
- if (sna->timer[FLUSH_TIMER] != -1)
- AddGeneralSocket(sna->timer[FLUSH_TIMER]);
+ int n;
- if (sna->timer[EXPIRE_TIMER] != -1)
- AddGeneralSocket(sna->timer[EXPIRE_TIMER]);
+ for (n = 0; n < NUM_TIMERS; n++) {
+ if (sna->timer[n] != -1)
+ AddGeneralSocket(sna->timer[n]);
+ }
}
Bool sna_accel_pre_init(struct sna *sna)
@@ -8552,8 +8629,11 @@ Bool sna_accel_init(ScreenPtr screen, struct sna *sna)
screen->RealizeFont = sna_realize_font;
screen->UnrealizeFont = sna_unrealize_font;
- list_init(&sna->dirty_pixmaps);
list_init(&sna->deferred_free);
+ list_init(&sna->dirty_pixmaps);
+ list_init(&sna->active_pixmaps);
+ list_init(&sna->inactive_clock[0]);
+ list_init(&sna->inactive_clock[1]);
AddGeneralSocket(sna->kgem.fd);
sna_accel_install_timers(sna);
@@ -8673,6 +8753,9 @@ void sna_accel_block_handler(struct sna *sna)
if (sna_accel_do_expire(sna))
sna_accel_expire(sna);
+
+ if (sna_accel_do_inactive(sna))
+ sna_accel_inactive(sna);
}
void sna_accel_wakeup_handler(struct sna *sna)