lima,panfrost: Move lima_tiling.c/h to /src/panfrost
[mesa.git] / src / gallium / drivers / panfrost / pan_allocate.c
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 }