zink: prevent ballooning of view object memory
authorMike Blumenkrantz <michael.blumenkrantz@gmail.com>
Wed, 12 Oct 2022 21:35:03 +0000 (17:35 -0400)
committerMarge Bot <emma+marge@anholt.net>
Thu, 13 Oct 2022 03:56:02 +0000 (03:56 +0000)
if a resource is in use every frame and never goes idle, it becomes
impossible to execute pruning, as there is no tracking for when views
are no longer in use

to avoid eventually ooming in this scenario, add some data to zink_resource_object
which can effectively "queue" pruning of these views if ballooning is
detected at a time when the views are guaranteed to be safe to delete

Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/19056>

src/gallium/drivers/zink/zink_batch.c
src/gallium/drivers/zink/zink_types.h

index 39ce61c..38bac79 100644 (file)
@@ -14,6 +14,8 @@
 #endif
 #include "wsi_common.h"
 
+#define MAX_VIEW_COUNT 500
+
 void
 debug_describe_zink_batch_state(char *buf, const struct zink_batch_state *ptr)
 {
@@ -36,6 +38,20 @@ reset_obj(struct zink_screen *screen, struct zink_batch_state *bs, struct zink_r
          while (util_dynarray_contains(&obj->views, VkImageView))
             VKSCR(DestroyImageView)(screen->dev, util_dynarray_pop(&obj->views, VkImageView), NULL);
       }
+      obj->view_prune_count = 0;
+      obj->view_prune_timeline = 0;
+      simple_mtx_unlock(&obj->view_lock);
+   } else if (util_dynarray_num_elements(&obj->views, VkBufferView) > MAX_VIEW_COUNT && !zink_bo_has_unflushed_usage(obj->bo)) {
+      /* avoid ballooning from too many views on always-used resources: */
+      simple_mtx_lock(&obj->view_lock);
+      /* ensure no existing view pruning is queued, double check elements in case pruning just finished */
+      if (!obj->view_prune_timeline && util_dynarray_num_elements(&obj->views, VkBufferView) > MAX_VIEW_COUNT) {
+         /* prune all existing views */
+         obj->view_prune_count = util_dynarray_num_elements(&obj->views, VkBufferView);
+         /* prune them when the views will definitely not be in use */
+         obj->view_prune_timeline = MAX2(obj->bo->reads ? obj->bo->reads->usage : 0,
+                                         obj->bo->writes ? obj->bo->writes->usage : 0);
+      }
       simple_mtx_unlock(&obj->view_lock);
    }
    util_dynarray_append(&bs->unref_resources, struct zink_resource_object*, obj);
@@ -133,6 +149,31 @@ unref_resources(struct zink_screen *screen, struct zink_batch_state *bs)
 {
    while (util_dynarray_contains(&bs->unref_resources, struct zink_resource_object*)) {
       struct zink_resource_object *obj = util_dynarray_pop(&bs->unref_resources, struct zink_resource_object*);
+      if (obj->view_prune_timeline && zink_screen_check_last_finished(screen, obj->view_prune_timeline)) {
+         simple_mtx_lock(&obj->view_lock);
+         /* check again under lock in case multi-context use is in the same place */
+         if (obj->view_prune_timeline && zink_screen_check_last_finished(screen, obj->view_prune_timeline)) {
+            /* prune `view_prune_count` views */
+            if (obj->is_buffer) {
+               VkBufferView *views = obj->views.data;
+               for (unsigned i = 0; i < obj->view_prune_count; i++)
+                  VKSCR(DestroyBufferView)(screen->dev, views[i], NULL);
+            } else {
+               VkImageView *views = obj->views.data;
+               for (unsigned i = 0; i < obj->view_prune_count; i++)
+                  VKSCR(DestroyImageView)(screen->dev, views[i], NULL);
+            }
+            size_t offset = obj->view_prune_count * sizeof(VkBufferView);
+            uint8_t *data = obj->views.data;
+            /* shift the view array to the start */
+            memcpy(data, data + offset, obj->views.size - offset);
+            /* adjust the array size */
+            obj->views.size -= offset;
+            obj->view_prune_count = 0;
+            obj->view_prune_timeline = 0;
+         }
+         simple_mtx_unlock(&obj->view_lock);
+      }
       zink_resource_object_reference(screen, &obj, NULL);
    }
    while (util_dynarray_contains(&bs->unref_semaphores, VkSemaphore))
index c66397c..67a1da0 100644 (file)
@@ -959,6 +959,8 @@ struct zink_resource_object {
 
    VkBuffer storage_buffer;
    simple_mtx_t view_lock;
+   uint32_t view_prune_count; //how many views to prune
+   uint32_t view_prune_timeline; //when to prune
    struct util_dynarray views;
 
    union {