Commit 25c3cae3 authored by Alyssa Rosenzweig's avatar Alyssa Rosenzweig 💜

panfrost: Manage texture memory with pb_slab

Previously, we allocated a single 256MB slab of texture memory on init,
never freed anything, and hoped it was enough. This is bad on so many
levels.

This patch uses the pb_slab interface courtesy of AMD to manage texture
memory. With the new API, we don't allocate any slabs on startup; we
instead provide a callback for allocating a new slab of memory, which
we implement with the existing kbase API. Then, we use its methods to
allocate and free texture memory.

For real-world motivation, without this patch, running
sway+es2gears (via Panfrost/llvmpipe respectively) would allocate 50% of
my memory, and then crash after a short amount of time due to OOM
conditions. With this patch, the same combination allocates about 30% of
memory and can run essentially indefinitely, since we're freeing
resources appropriately.

Future patches will extend this interface for other parts of the
cmdstream to further reduce our memory footprint.
parent ecfa6722
......@@ -44,6 +44,9 @@
#include "pan_blend_shaders.h"
#include "pan_wallpaper.h"
/* Texture memory */
#define HEAP_TEXTURE 0
static void
panfrost_flush(
struct pipe_context *pipe,
......@@ -2694,7 +2697,20 @@ panfrost_tile_texture(struct panfrost_context *ctx, struct panfrost_resource *rs
int swizzled_sz = panfrost_swizzled_size(width, height, rsrc->bytes_per_pixel);
/* Allocate the transfer given that known size but do not copy */
uint8_t *swizzled = panfrost_allocate_transfer(&ctx->textures, swizzled_sz, &rsrc->gpu[level]);
struct pb_slab_entry *entry = pb_slab_alloc(&ctx->slabs, swizzled_sz, HEAP_TEXTURE);
struct panfrost_memory_entry *p_entry = (struct panfrost_memory_entry *) entry;
struct panfrost_memory *backing = (struct panfrost_memory *) entry->slab;
uint8_t *swizzled = backing->cpu + p_entry->offset;
/* Save the entry. But if there was already an entry here (from a
* previous upload of the resource), free that one so we don't leak */
if (rsrc->entry[level] != NULL) {
p_entry->freed = true;
pb_slab_free(&ctx->slabs, &p_entry->base);
}
rsrc->entry[level] = p_entry;
if (rsrc->tiled) {
/* Run actual texture swizzle, writing directly to the mapped
......@@ -2885,7 +2901,6 @@ panfrost_setup_hardware(struct panfrost_context *ctx)
panfrost_allocate_slab(ctx, &ctx->cmdstream_rings[i], 8 * 64 * 8 * 16, true, true, 0, 0, 0);
panfrost_allocate_slab(ctx, &ctx->cmdstream_persistent, 8 * 64 * 8 * 2, true, true, 0, 0, 0);
panfrost_allocate_slab(ctx, &ctx->textures, 4 * 64 * 64 * 4, true, true, 0, 0, 0);
panfrost_allocate_slab(ctx, &ctx->scratchpad, 64, true, true, 0, 0, 0);
panfrost_allocate_slab(ctx, &ctx->varying_mem, 16384, false, true, 0, 0, 0);
panfrost_allocate_slab(ctx, &ctx->shaders, 4096, true, false, MALI_MEM_PROT_GPU_EX, 0, 0);
......@@ -2905,6 +2920,52 @@ static const struct u_transfer_vtbl transfer_vtbl = {
//.get_stencil = panfrost_resource_get_stencil,
};
static struct pb_slab *
panfrost_slab_alloc(void *priv, unsigned heap, unsigned entry_size, unsigned group_index)
{
struct panfrost_context *ctx = (struct panfrost_context *) priv;
struct panfrost_memory *mem = CALLOC_STRUCT(panfrost_memory);
size_t slab_size = (1 << 25); /* One greater than the max entry size */
mem->slab.num_entries = slab_size / entry_size;
mem->slab.num_free = mem->slab.num_entries;
LIST_INITHEAD(&mem->slab.free);
for (unsigned i = 0; i < mem->slab.num_entries; ++i) {
/* Create a slab entry */
struct panfrost_memory_entry *entry = CALLOC_STRUCT(panfrost_memory_entry);
entry->offset = entry_size * i;
entry->base.slab = &mem->slab;
entry->base.group_index = group_index;
LIST_ADDTAIL(&entry->base.head, &mem->slab.free);
}
/* Actually allocate the memory from kernel-space. Mapped, same_va, no
* special flags */
panfrost_allocate_slab(ctx, mem, slab_size / 4096, true, true, 0, 0, 0);
return &mem->slab;
}
static bool
panfrost_slab_can_reclaim(void *priv, struct pb_slab_entry *entry)
{
struct panfrost_memory_entry *p_entry = (struct panfrost_memory_entry *) entry;
return p_entry->freed;
}
static void
panfrost_slab_free(void *priv, struct pb_slab *slab)
{
/* STUB */
//struct panfrost_memory *mem = (struct panfrost_memory *) slab;
printf("stub: Tried to free slab\n");
}
/* New context creation, which also does hardware initialisation since I don't
* know the better way to structure this :smirk: */
......@@ -3014,6 +3075,20 @@ panfrost_create_context(struct pipe_screen *screen, void *priv, unsigned flags)
ctx->blitter = util_blitter_create(gallium);
assert(ctx->blitter);
pb_slabs_init(&ctx->slabs,
/* slabs from 2^min to 2^max */
10, /* 2^10 = 1024 */
24, /* 2^24 = 16 MB, why would you need more? T_T */
1, /* We only have one heap for now (texture memory) */
ctx,
panfrost_slab_can_reclaim,
panfrost_slab_alloc,
panfrost_slab_free);
/* Prepare for render! */
panfrost_setup_hardware(ctx);
......
......@@ -90,7 +90,6 @@ struct panfrost_context {
struct panfrost_memory cmdstream;
struct panfrost_memory cmdstream_persistent;
struct panfrost_memory textures;
struct panfrost_memory shaders;
struct panfrost_memory scratchpad;
struct panfrost_memory tiler_heap;
......@@ -193,6 +192,9 @@ struct panfrost_context {
struct pipe_blend_color blend_color;
struct pipe_depth_stencil_alpha_state *depth_stencil;
struct pipe_stencil_ref stencil_ref;
/* Memory management is based on subdividing slabs with AMD's allocator */
struct pb_slabs slabs;
};
/* Corresponds to the CSO */
......@@ -291,6 +293,9 @@ struct panfrost_resource {
mali_ptr gpu[MAX_MIP_LEVELS];
/* Memory entry corresponding to gpu above */
struct panfrost_memory_entry *entry[MAX_MIP_LEVELS];
/* Is something other than level 0 ever written? */
bool is_mipmap;
......
......@@ -32,6 +32,7 @@
#include <panfrost-job.h>
#include <linux/ioctl.h>
#include "pan_slowfb.h"
#include "pipebuffer/pb_slab.h"
int pandev_open(void);
......@@ -49,12 +50,27 @@ void
panfrost_shader_compile(struct panfrost_context *ctx, struct mali_shader_meta *meta, const char *src, int type, struct panfrost_shader_state *state);
struct panfrost_memory {
/* Subclassing slab object */
struct pb_slab slab;
/* Backing for the slab in memory */
uint8_t *cpu;
mali_ptr gpu;
int stack_bottom;
size_t size;
};
struct panfrost_memory_entry {
/* Subclass */
struct pb_slab_entry base;
/* Have we been freed? */
bool freed;
/* Offset into the slab of the entry */
off_t offset;
};
/* Functions for replay */
mali_ptr pandev_upload(int cheating_offset, int *stack_bottom, mali_ptr base, void *base_map, const void *data, size_t sz, bool no_pad);
mali_ptr pandev_upload_sequential(mali_ptr base, void *base_map, const void *data, size_t sz);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment