#include <xf86drmMode.h>
#include "util/u_memory.h"
+#include "util/ralloc.h"
#include "vc4_context.h"
#include "vc4_screen.h"
+#define container_of(ptr, type, field) \
+ (type*)((char*)ptr - offsetof(type, field))
+
+static struct vc4_bo *
+vc4_bo_from_cache(struct vc4_screen *screen, uint32_t size, const char *name)
+{
+ struct vc4_bo_cache *cache = &screen->bo_cache;
+ uint32_t page_index = size / 4096 - 1;
+
+ if (cache->size_list_size <= page_index)
+ return NULL;
+
+ struct vc4_bo *bo = NULL;
+ pipe_mutex_lock(cache->lock);
+ if (!is_empty_list(&cache->size_list[page_index])) {
+ struct simple_node *node = last_elem(&cache->size_list[page_index]);
+ bo = container_of(node, struct vc4_bo, size_list);
+ pipe_reference_init(&bo->reference, 1);
+ remove_from_list(&bo->time_list);
+ remove_from_list(&bo->size_list);
+
+ bo->name = name;
+ }
+ pipe_mutex_unlock(cache->lock);
+ return bo;
+}
+
struct vc4_bo *
vc4_bo_alloc(struct vc4_screen *screen, uint32_t size, const char *name)
{
- struct vc4_bo *bo = CALLOC_STRUCT(vc4_bo);
+ struct vc4_bo *bo;
+ size = align(size, 4096);
+
+ bo = vc4_bo_from_cache(screen, size, name);
+ if (bo)
+ return bo;
+
+ bo = CALLOC_STRUCT(vc4_bo);
if (!bo)
return NULL;
bo->screen = screen;
bo->size = size;
bo->name = name;
+ bo->private = true;
struct drm_mode_create_dumb create;
memset(&create, 0, sizeof(create));
}
void
+vc4_bo_last_unreference(struct vc4_bo *bo)
+{
+ struct vc4_screen *screen = bo->screen;
+
+ struct timespec time;
+ clock_gettime(CLOCK_MONOTONIC, &time);
+ pipe_mutex_lock(screen->bo_cache.lock);
+ vc4_bo_last_unreference_locked_timed(bo, time.tv_sec);
+ pipe_mutex_unlock(screen->bo_cache.lock);
+}
+
+static void
vc4_bo_free(struct vc4_bo *bo)
{
struct vc4_screen *screen = bo->screen;
free(bo);
}
+static void
+free_stale_bos(struct vc4_screen *screen, time_t time)
+{
+ while (!is_empty_list(&screen->bo_cache.time_list)) {
+ struct simple_node *node =
+ first_elem(&screen->bo_cache.time_list);
+ struct vc4_bo *bo = container_of(node, struct vc4_bo, time_list);
+
+ /* If it's more than a second old, free it. */
+ if (time - bo->free_time > 2) {
+ remove_from_list(&bo->time_list);
+ remove_from_list(&bo->size_list);
+ vc4_bo_free(bo);
+ } else {
+ break;
+ }
+ }
+}
+
+void
+vc4_bo_last_unreference_locked_timed(struct vc4_bo *bo, time_t time)
+{
+ struct vc4_screen *screen = bo->screen;
+ struct vc4_bo_cache *cache = &screen->bo_cache;
+ uint32_t page_index = bo->size / 4096 - 1;
+
+ if (!bo->private) {
+ vc4_bo_free(bo);
+ return;
+ }
+
+ if (cache->size_list_size <= page_index) {
+ struct simple_node *new_list =
+ ralloc_array(screen, struct simple_node, page_index + 1);
+
+ /* Move old list contents over (since the array has moved, and
+ * therefore the pointers to the list heads have to change.
+ */
+ for (int i = 0; i < cache->size_list_size; i++) {
+ struct simple_node *old_head = &cache->size_list[i];
+ if (is_empty_list(old_head))
+ make_empty_list(&new_list[i]);
+ else {
+ new_list[i].next = old_head->next;
+ new_list[i].prev = old_head->prev;
+ new_list[i].next->prev = &new_list[i];
+ new_list[i].prev->next = &new_list[i];
+ }
+ }
+ for (int i = cache->size_list_size; i < page_index + 1; i++)
+ make_empty_list(&new_list[i]);
+
+ cache->size_list = new_list;
+ cache->size_list_size = page_index + 1;
+ }
+
+ bo->free_time = time;
+ insert_at_tail(&cache->size_list[page_index], &bo->size_list);
+ insert_at_tail(&cache->time_list, &bo->time_list);
+
+ free_stale_bos(screen, time);
+}
+
static struct vc4_bo *
vc4_bo_open_handle(struct vc4_screen *screen,
uint32_t winsys_stride,
bo->handle = handle;
bo->size = size;
bo->name = "winsys";
+ bo->private = false;
#ifdef USE_VC4_SIMULATOR
vc4_bo_map(bo);
return false;
}
+ bo->private = false;
*name = flink.name;
return true;
return map;
}
+
+void
+vc4_bufmgr_destroy(struct pipe_screen *pscreen)
+{
+ struct vc4_screen *screen = vc4_screen(pscreen);
+ struct vc4_bo_cache *cache = &screen->bo_cache;
+
+ while (!is_empty_list(&cache->time_list)) {
+ struct simple_node *node = first_elem(&cache->time_list);
+ struct vc4_bo *bo = container_of(node, struct vc4_bo, time_list);
+
+ remove_from_list(&bo->time_list);
+ remove_from_list(&bo->size_list);
+ vc4_bo_free(bo);
+ }
+}
#include <stdint.h>
#include "util/u_inlines.h"
+#include "vc4_qir.h"
struct vc4_context;
void *simulator_winsys_map;
uint32_t simulator_winsys_stride;
#endif
+
+ /** Entry in the linked list of buffers freed, by age. */
+ struct simple_node time_list;
+ /** Entry in the per-page-count linked list of buffers freed (by age). */
+ struct simple_node size_list;
+ /** Approximate second when the bo was freed. */
+ time_t free_time;
+ /**
+ * Whether only our process has a reference to the BO (meaning that
+ * it's safe to reuse it in the BO cache).
+ */
+ bool private;
};
struct vc4_bo *vc4_bo_alloc(struct vc4_screen *screen, uint32_t size,
const char *name);
struct vc4_bo *vc4_bo_alloc_mem(struct vc4_screen *screen, const void *data,
uint32_t size, const char *name);
-void vc4_bo_free(struct vc4_bo *bo);
+void vc4_bo_last_unreference(struct vc4_bo *bo);
+void vc4_bo_last_unreference_locked_timed(struct vc4_bo *bo, time_t time);
struct vc4_bo *vc4_bo_open_name(struct vc4_screen *screen, uint32_t name,
uint32_t winsys_stride);
struct vc4_bo *vc4_bo_open_dmabuf(struct vc4_screen *screen, int fd,
vc4_bo_set_reference(struct vc4_bo **old_bo, struct vc4_bo *new_bo)
{
if (pipe_reference(&(*old_bo)->reference, &new_bo->reference))
- vc4_bo_free(*old_bo);
+ vc4_bo_last_unreference(*old_bo);
*old_bo = new_bo;
}
return;
if (pipe_reference(&(*bo)->reference, NULL))
- vc4_bo_free(*bo);
+ vc4_bo_last_unreference(*bo);
+ *bo = NULL;
+}
+
+static inline void
+vc4_bo_unreference_locked_timed(struct vc4_bo **bo, time_t time)
+{
+ if (!*bo)
+ return;
+
+ if (pipe_reference(&(*bo)->reference, NULL))
+ vc4_bo_last_unreference_locked_timed(*bo, time);
*bo = NULL;
}
bool
vc4_wait_seqno(struct vc4_screen *screen, uint64_t seqno, uint64_t timeout_ns);
+void
+vc4_bufmgr_destroy(struct pipe_screen *pscreen);
+
#endif /* VC4_BUFMGR_H */