panfrost: Cache index buffer bounds

Submitted by Alyssa Rosenzweig on March 24, 2019, 5:15 a.m.

Details

Message ID 20190324051505.10367-1-alyssa@rosenzweig.io
State New
Headers show
Series "panfrost: Cache index buffer bounds" ( rev: 1 ) in Mesa

Not browsing as part of any series.

Commit Message

Alyssa Rosenzweig March 24, 2019, 5:15 a.m.
This code is probably a wholesale duplication of the corresponding code
in mesa/src/vbo/vbo_minmax_indices.c; we should investigate reusing
that.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
---
 src/gallium/drivers/panfrost/pan_context.c  | 108 ++++++++++++++------
 src/gallium/drivers/panfrost/pan_resource.c |  37 ++++++-
 src/gallium/drivers/panfrost/pan_resource.h |   9 ++
 3 files changed, 117 insertions(+), 37 deletions(-)

Patch hide | download patch | download mbox

diff --git a/src/gallium/drivers/panfrost/pan_context.c b/src/gallium/drivers/panfrost/pan_context.c
index 19cdb6c0444..2e8a0f03b1a 100644
--- a/src/gallium/drivers/panfrost/pan_context.c
+++ b/src/gallium/drivers/panfrost/pan_context.c
@@ -1355,17 +1355,82 @@  panfrost_translate_index_size(unsigned size)
         }
 }
 
-static const uint8_t *
-panfrost_get_index_buffer_raw(const struct pipe_draw_info *info)
+#define CALCULATE_MIN_MAX_INDEX(T, buffer, start, count) \
+        for (unsigned _idx = (start); _idx < (start + count); ++_idx) { \
+                T idx = buffer[_idx]; \
+                if (idx > max_index) max_index = idx; \
+                if (idx < min_index) min_index = idx; \
+        }
+
+/* Computes the index bounds, rescanning if necessary */
+
+static void
+panfrost_get_index_bounds(
+                const struct pipe_draw_info *info,
+                int *o_min_index,
+                int *o_max_index)
 {
-        if (info->has_user_indices) {
-                return (const uint8_t *) info->index.user;
+        int min_index = INT_MAX, max_index = 0;
+
+        struct panfrost_resource *rsrc =
+                info->has_user_indices ? NULL :
+                pan_resource(info->index.resource);
+
+        if (rsrc) {
+                assert(rsrc->bo);
+        }
+
+        uint64_t key = ((uint64_t) info->start << 32) | ((uint32_t) info->count);
+
+        /* Reuse bounds from cache if possible */
+        if (rsrc) {
+                struct panfrost_indices *cached = (struct panfrost_indices *)
+                        _mesa_hash_table_u64_search(rsrc->indices, key);
+
+                if (cached) {
+                        *o_min_index = cached->min;
+                        *o_max_index = cached->max;
+                        return;
+                }
+        }
+
+        const uint8_t *ibuf8 =
+                rsrc ? rsrc->bo->cpu[0] : info->index.user;
+
+        /* Scan the buffer */
+
+        if (info->index_size == 1) {
+                CALCULATE_MIN_MAX_INDEX(uint8_t, ibuf8, info->start, info->count);
+        } else if (info->index_size == 2) {
+                const uint16_t *ibuf16 = (const uint16_t *) ibuf8;
+                CALCULATE_MIN_MAX_INDEX(uint16_t, ibuf16, info->start, info->count);
+        } else if (info->index_size == 4) {
+                const uint32_t *ibuf32 = (const uint32_t *) ibuf8;
+                CALCULATE_MIN_MAX_INDEX(uint32_t, ibuf32, info->start, info->count);
         } else {
-                struct panfrost_resource *rsrc = (struct panfrost_resource *) (info->index.resource);
-                return (const uint8_t *) rsrc->bo->cpu[0];
+                assert(0);
+        }
+
+        /* Make sure we didn't go crazy */
+        assert(min_index < INT_MAX);
+        assert(max_index > 0);
+        assert(max_index > min_index);
+
+        /* Write bounds back */
+        *o_min_index = min_index;
+        *o_max_index = max_index;
+
+        /* Write bounds to cache */
+        if (rsrc) {
+                struct panfrost_indices *ind = CALLOC_STRUCT(panfrost_indices);
+                ind->min = min_index;
+                ind->max = max_index;
+                _mesa_hash_table_u64_insert(rsrc->indices, key, ind);
         }
 }
 
+#undef CALCULATE_MIN_MAX_INDEX
+
 /* Gets a GPU address for the associated index buffer. Only gauranteed to be
  * good for the duration of the draw (transient), could last longer */
 
@@ -1381,18 +1446,11 @@  panfrost_get_index_buffer_mapped(struct panfrost_context *ctx, const struct pipe
                 return rsrc->bo->gpu[0] + offset;
         } else {
                 /* Otherwise, we need to upload to transient memory */
-                const uint8_t *ibuf8 = panfrost_get_index_buffer_raw(info);
+                const uint8_t *ibuf8 = (const uint8_t *) info->index.user;
                 return panfrost_upload_transient(ctx, ibuf8 + offset, info->count * info->index_size);
         }
 }
 
-#define CALCULATE_MIN_MAX_INDEX(T, buffer, start, count) \
-        for (unsigned _idx = (start); _idx < (start + count); ++_idx) { \
-                T idx = buffer[_idx]; \
-                if (idx > max_index) max_index = idx; \
-                if (idx < min_index) min_index = idx; \
-        }
-
 static void
 panfrost_draw_vbo(
         struct pipe_context *pipe,
@@ -1445,27 +1503,9 @@  panfrost_draw_vbo(
                 /* Calculate the min/max index used so we can figure out how
                  * many times to invoke the vertex shader */
 
-                const uint8_t *ibuf8 = panfrost_get_index_buffer_raw(info);
-
-                int min_index = INT_MAX;
-                int max_index = 0;
-
-                if (info->index_size == 1) {
-                        CALCULATE_MIN_MAX_INDEX(uint8_t, ibuf8, info->start, info->count);
-                } else if (info->index_size == 2) {
-                        const uint16_t *ibuf16 = (const uint16_t *) ibuf8;
-                        CALCULATE_MIN_MAX_INDEX(uint16_t, ibuf16, info->start, info->count);
-                } else if (info->index_size == 4) {
-                        const uint32_t *ibuf32 = (const uint32_t *) ibuf8;
-                        CALCULATE_MIN_MAX_INDEX(uint32_t, ibuf32, info->start, info->count);
-                } else {
-                        assert(0);
-                }
+                int min_index, max_index;
 
-                /* Make sure we didn't go crazy */
-                assert(min_index < INT_MAX);
-                assert(max_index > 0);
-                assert(max_index > min_index);
+                panfrost_get_index_bounds(info, &min_index, &max_index);
 
                 /* Use the corresponding values */
                 invocation_count = max_index - min_index + 1;
diff --git a/src/gallium/drivers/panfrost/pan_resource.c b/src/gallium/drivers/panfrost/pan_resource.c
index 6fe871594ea..8bf242d7e94 100644
--- a/src/gallium/drivers/panfrost/pan_resource.c
+++ b/src/gallium/drivers/panfrost/pan_resource.c
@@ -282,6 +282,13 @@  panfrost_resource_create(struct pipe_screen *screen,
                         assert(0);
         }
 
+        /* Allocate room for index buffer cache */
+
+        if (template->bind & PIPE_BIND_INDEX_BUFFER) {
+                /* TODO leaks */
+                so->indices = _mesa_hash_table_u64_create(NULL);
+        }
+
         if (template->bind & PIPE_BIND_DISPLAY_TARGET ||
             template->bind & PIPE_BIND_SCANOUT ||
             template->bind & PIPE_BIND_SHARED) {
@@ -353,6 +360,12 @@  panfrost_destroy_bo(struct panfrost_screen *screen, struct panfrost_bo *pbo)
         }
 }
 
+static void
+panfrost_delete_entry(struct hash_entry *entry)
+{
+        free(entry->data);
+}
+
 static void
 panfrost_resource_destroy(struct pipe_screen *screen,
                           struct pipe_resource *pt)
@@ -366,6 +379,9 @@  panfrost_resource_destroy(struct pipe_screen *screen,
 	if (rsrc->bo)
 		panfrost_destroy_bo(pscreen, rsrc->bo);
 
+        if (rsrc->indices)
+                _mesa_hash_table_u64_destroy(rsrc->indices, panfrost_delete_entry);
+
 	FREE(rsrc);
 }
 
@@ -377,9 +393,16 @@  panfrost_map_bo(struct panfrost_context *ctx, struct pipe_transfer *transfer)
         /* If non-zero level, it's a mipmapped resource and needs to be treated as such */
         bo->is_mipmap |= transfer->level;
 
-        if (transfer->usage & PIPE_TRANSFER_MAP_DIRECTLY && bo->layout != PAN_LINEAR) {
-                /* We can only directly map linear resources */
-                return NULL;
+        if (transfer->usage & PIPE_TRANSFER_MAP_DIRECTLY) {
+                if (bo->layout != PAN_LINEAR) {
+                        /* We can only directly map linear resources */
+                        return NULL;
+                }
+
+                if (transfer->resource->bind & PIPE_BIND_INDEX_BUFFER) {
+                        /* Direct mapping messes with index buffer scanning */
+                        return NULL;
+                }
         }
 
         if (transfer->resource->bind & PIPE_BIND_DEPTH_STENCIL) {
@@ -490,6 +513,14 @@  panfrost_unmap_bo(struct panfrost_context *ctx,
                                 panfrost_tile_texture(screen, prsrc, transfer->level);
                         }
                 }
+
+                /* Invalidate index buffer scans, TODO leaks */
+                struct panfrost_resource *r = pan_resource(transfer->resource);
+
+                if (r->indices) {
+                        _mesa_hash_table_u64_destroy(r->indices, panfrost_delete_entry);
+                        r->indices = _mesa_hash_table_u64_create(NULL);
+                }
         }
 }
 
diff --git a/src/gallium/drivers/panfrost/pan_resource.h b/src/gallium/drivers/panfrost/pan_resource.h
index 4b5b9c49c07..e6fb7cda5a2 100644
--- a/src/gallium/drivers/panfrost/pan_resource.h
+++ b/src/gallium/drivers/panfrost/pan_resource.h
@@ -84,6 +84,12 @@  struct panfrost_bo {
         unsigned int stride;
 };
 
+/* Container for cached index buffer bounds */
+struct panfrost_indices {
+        /* Calculated bounds */
+        uint32_t min, max;
+};
+
 struct panfrost_resource {
         struct pipe_resource base;
 
@@ -91,6 +97,9 @@  struct panfrost_resource {
         struct renderonly_scanout *scanout;
 
         struct panfrost_resource *separate_stencil;
+
+        /* Hash mapping (start<<32 | count) to struct panfrost_indices */
+        struct hash_table_u64 *indices;
 };
 
 static inline struct panfrost_resource *

Comments


> Can you reuse u_vbuf_get_minmax_index()?

From a preliminary read, it didn't look like that did caching?

> It doesn't, but the inside of your caching function should probably be
> using it.

Ah, I see, fair point :)

Will we want this caching to be moved up to shared Gallium or no?