1 /* 2 * Copyright 2018 Alyssa Rosenzweig 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining a 5 * copy of this software and associated documentation files (the "Software"), 6 * to deal in the Software without restriction, including without limitation 7 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 * and/or sell copies of the Software, and to permit persons to whom the 9 * Software is furnished to do so, subject to the following conditions: 10 * 11 * The above copyright notice and this permission notice (including the next 12 * paragraph) shall be included in all copies or substantial portions of the 13 * Software. 14 * 15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 * SOFTWARE. 22 * 23 */ 24 25 #include <stdio.h> 26 #include <stdlib.h> 27 #include <string.h> 28 #include <assert.h> 29 #include <panfrost-misc.h> 30 #include <panfrost-job.h> 31 #include "pan_context.h" 32 33 /* TODO: What does this actually have to be? */ 34 #define ALIGNMENT 128 35 36 /* Allocate a mapped chunk directly from a heap */ 37 38 struct panfrost_transfer 39 panfrost_allocate_chunk(struct panfrost_context *ctx, size_t size, unsigned heap_id) 40 { 41 size = ALIGN(size, ALIGNMENT); 42 43 struct pipe_context *gallium = (struct pipe_context *) ctx; 44 struct panfrost_screen *screen = pan_screen(gallium->screen); 45 46 struct pb_slab_entry *entry = pb_slab_alloc(&screen->slabs, size, heap_id); 47 struct panfrost_memory_entry *p_entry = (struct panfrost_memory_entry *) entry; 48 struct panfrost_memory *backing = (struct panfrost_memory *) entry->slab; 49 50 struct panfrost_transfer transfer = { 51 .cpu = backing->cpu + p_entry->offset, 52 .gpu = backing->gpu + p_entry->offset 53 }; 54 55 return transfer; 56 } 57 58 /* Transient command stream pooling: command stream uploads try to simply copy 59 * into whereever we left off. If there isn't space, we allocate a new entry 60 * into the pool and copy there */ 61 62 struct panfrost_transfer 63 panfrost_allocate_transient(struct panfrost_context *ctx, size_t sz) 64 { 65 /* Pad the size */ 66 sz = ALIGN(sz, ALIGNMENT); 67 68 /* Check if there is room in the current entry */ 69 struct panfrost_transient_pool *pool = &ctx->transient_pools[ctx->cmdstream_i]; 70 71 if ((pool->entry_offset + sz) > pool->entry_size) { 72 /* Don't overflow this entry -- advance to the next */ 73 74 pool->entry_offset = 0; 75 76 pool->entry_index++; 77 assert(pool->entry_index < PANFROST_MAX_TRANSIENT_ENTRIES); 78 79 /* Check if this entry exists */ 80 81 if (pool->entry_index >= pool->entry_count) { 82 /* Don't overflow the pool -- allocate a new one */ 83 struct pipe_context *gallium = (struct pipe_context *) ctx; 84 struct panfrost_screen *screen = pan_screen(gallium->screen); 85 struct pb_slab_entry *entry = pb_slab_alloc(&screen->slabs, pool->entry_size, HEAP_TRANSIENT); 86 87 pool->entry_count++; 88 pool->entries[pool->entry_index] = (struct panfrost_memory_entry *) entry; 89 } 90 91 /* Make sure we -still- won't overflow */ 92 assert(sz < pool->entry_size); 93 } 94 95 /* We have an entry we can write to, so do the upload! */ 96 struct panfrost_memory_entry *p_entry = pool->entries[pool->entry_index]; 97 struct panfrost_memory *backing = (struct panfrost_memory *) p_entry->base.slab; 98 99 struct panfrost_transfer ret = { 100 .cpu = backing->cpu + p_entry->offset + pool->entry_offset, 101 .gpu = backing->gpu + p_entry->offset + pool->entry_offset 102 }; 103 104 /* Advance the pointer */ 105 pool->entry_offset += sz; 106 107 return ret; 108 109 } 110 111 mali_ptr 112 panfrost_upload_transient(struct panfrost_context *ctx, const void *data, size_t sz) 113 { 114 struct panfrost_transfer transfer = panfrost_allocate_transient(ctx, sz); 115 memcpy(transfer.cpu, data, sz); 116 return transfer.gpu; 117 } 118 119 // TODO: An actual allocator, perhaps 120 // TODO: Multiple stacks for multiple bases? 121 122 int hack_stack_bottom = 4096; /* Don't interfere with constant offsets */ 123 int last_offset = 0; 124 125 static inline int 126 pandev_allocate_offset(int *stack, size_t sz) 127 { 128 /* First, align the stack bottom to something nice; it's not critical 129 * at this point if we waste a little space to do so. */ 130 131 int excess = *stack & (ALIGNMENT - 1); 132 133 /* Add the secret of my */ 134 if (excess) 135 *stack += ALIGNMENT - excess; 136 137 /* Finally, use the new bottom for the allocation and move down the 138 * stack */ 139 140 int ret = *stack; 141 *stack += sz; 142 return ret; 143 } 144 145 inline mali_ptr 146 pandev_upload(int cheating_offset, int *stack_bottom, mali_ptr base, void *base_map, const void *data, size_t sz, bool no_pad) 147 { 148 int offset; 149 150 /* We're not positive about the sizes of all objects, but we don't want 151 * them to crash against each other either. Let the caller disable 152 * padding if they so choose, though. */ 153 154 size_t padded_size = no_pad ? sz : sz * 2; 155 156 /* If no specific bottom is specified, use a global one... don't do 157 * this in production, kids */ 158 159 if (!stack_bottom) 160 stack_bottom = &hack_stack_bottom; 161 162 /* Allocate space for the new GPU object, if required */ 163 164 if (cheating_offset == -1) { 165 offset = pandev_allocate_offset(stack_bottom, padded_size); 166 } else { 167 offset = cheating_offset; 168 *stack_bottom = offset + sz; 169 } 170 171 /* Save last offset for sequential uploads (job descriptors) */ 172 last_offset = offset + padded_size; 173 174 /* Upload it */ 175 memcpy((uint8_t *) base_map + offset, data, sz); 176 177 /* Return the GPU address */ 178 return base + offset; 179 } 180 181 /* Upload immediately after the last allocation */ 182 183 mali_ptr 184 pandev_upload_sequential(mali_ptr base, void *base_map, const void *data, size_t sz) 185 { 186 return pandev_upload(last_offset, NULL, base, base_map, data, sz, /* false */ true); 187 } 188 189 /* Simplified APIs for the real driver, rather than replays */ 190 191 mali_ptr 192 panfrost_upload(struct panfrost_memory *mem, const void *data, size_t sz, bool no_pad) 193 { 194 /* Bounds check */ 195 if ((mem->stack_bottom + sz) >= mem->size) { 196 printf("Out of memory, tried to upload %zd but only %zd available\n", sz, mem->size - mem->stack_bottom); 197 assert(0); 198 } 199 200 return pandev_upload(-1, &mem->stack_bottom, mem->gpu, mem->cpu, data, sz, no_pad); 201 } 202 203 mali_ptr 204 panfrost_upload_sequential(struct panfrost_memory *mem, const void *data, size_t sz) 205 { 206 return pandev_upload(last_offset, &mem->stack_bottom, mem->gpu, mem->cpu, data, sz, true); 207 } 208 209 /* Simplified interface to allocate a chunk without any upload, to allow 210 * zero-copy uploads. This is particularly useful when the copy would happen 211 * anyway, for instance with texture swizzling. */ 212 213 void * 214 panfrost_allocate_transfer(struct panfrost_memory *mem, size_t sz, mali_ptr *gpu) 215 { 216 int offset = pandev_allocate_offset(&mem->stack_bottom, sz); 217 218 *gpu = mem->gpu + offset; 219 return mem->cpu + offset; 220 } 221