panfrost: Take into account flags when looking up in the BO cache
[mesa.git] / src / gallium / drivers / panfrost / pan_allocate.c
1 /*
2 * © Copyright 2018 Alyssa Rosenzweig
3 * Copyright (C) 2019 Collabora, Ltd.
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 * and/or sell copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice (including the next
13 * paragraph) shall be included in all copies or substantial portions of the
14 * Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 * SOFTWARE.
23 *
24 */
25
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <assert.h>
30 #include <panfrost-misc.h>
31 #include <panfrost-job.h>
32 #include "pan_context.h"
33
34 /* TODO: What does this actually have to be? */
35 #define ALIGNMENT 128
36
37 /* Allocate a new transient slab */
38
39 static struct panfrost_bo *
40 panfrost_create_slab(struct panfrost_screen *screen, unsigned *index)
41 {
42 /* Allocate a new slab on the screen */
43
44 struct panfrost_bo **new =
45 util_dynarray_grow(&screen->transient_bo,
46 struct panfrost_bo *, 1);
47
48 struct panfrost_bo *alloc = panfrost_drm_create_bo(screen, TRANSIENT_SLAB_SIZE, 0);
49
50 *new = alloc;
51
52 /* Return the BO as well as the index we just added */
53
54 *index = util_dynarray_num_elements(&screen->transient_bo, void *) - 1;
55 return alloc;
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 struct panfrost_screen *screen = pan_screen(ctx->base.screen);
66 struct panfrost_job *batch = panfrost_get_job_for_fbo(ctx);
67
68 /* Pad the size */
69 sz = ALIGN_POT(sz, ALIGNMENT);
70
71 /* Find or create a suitable BO */
72 struct panfrost_bo *bo = NULL;
73
74 unsigned offset = 0;
75 bool update_offset = false;
76
77 bool has_current = batch->transient_indices.size;
78 bool fits_in_current = (batch->transient_offset + sz) < TRANSIENT_SLAB_SIZE;
79
80 if (likely(has_current && fits_in_current)) {
81 /* We can reuse the topmost BO, so get it */
82 unsigned idx = util_dynarray_top(&batch->transient_indices, unsigned);
83 bo = pan_bo_for_index(screen, idx);
84
85 /* Use the specified offset */
86 offset = batch->transient_offset;
87 update_offset = true;
88 } else if (sz < TRANSIENT_SLAB_SIZE) {
89 /* We can't reuse the topmost BO, but we can get a new one.
90 * First, look for a free slot */
91
92 unsigned count = util_dynarray_num_elements(&screen->transient_bo, void *);
93 unsigned index = 0;
94
95 unsigned free = __bitset_ffs(
96 screen->free_transient,
97 count / BITSET_WORDBITS);
98
99 if (likely(free)) {
100 /* Use this one */
101 index = free - 1;
102
103 /* It's ours, so no longer free */
104 BITSET_CLEAR(screen->free_transient, index);
105
106 /* Grab the BO */
107 bo = pan_bo_for_index(screen, index);
108 } else {
109 /* Otherwise, create a new BO */
110 bo = panfrost_create_slab(screen, &index);
111 }
112
113 /* Remember we created this */
114 util_dynarray_append(&batch->transient_indices, unsigned, index);
115
116 update_offset = true;
117 } else {
118 /* Create a new BO and reference it */
119 bo = panfrost_drm_create_bo(screen, ALIGN_POT(sz, 4096), 0);
120 panfrost_job_add_bo(batch, bo);
121
122 /* Creating a BO adds a reference, and then the job adds a
123 * second one. So we need to pop back one reference */
124 panfrost_bo_unreference(&screen->base, bo);
125 }
126
127 struct panfrost_transfer ret = {
128 .cpu = bo->cpu + offset,
129 .gpu = bo->gpu + offset,
130 };
131
132 if (update_offset)
133 batch->transient_offset = offset + sz;
134
135 return ret;
136
137 }
138
139 mali_ptr
140 panfrost_upload_transient(struct panfrost_context *ctx, const void *data, size_t sz)
141 {
142 struct panfrost_transfer transfer = panfrost_allocate_transient(ctx, sz);
143 memcpy(transfer.cpu, data, sz);
144 return transfer.gpu;
145 }
146
147 /* The code below is exclusively for the use of shader memory and is subject to
148 * be rewritten soon enough since it never frees the memory it allocates. Here
149 * be dragons, etc. */
150
151 mali_ptr
152 panfrost_upload(struct panfrost_memory *mem, const void *data, size_t sz)
153 {
154 size_t aligned_sz = ALIGN_POT(sz, ALIGNMENT);
155
156 /* Bounds check */
157 if ((mem->stack_bottom + aligned_sz) >= mem->bo->size) {
158 printf("Out of memory, tried to upload %zd but only %zd available\n",
159 sz, mem->bo->size - mem->stack_bottom);
160 assert(0);
161 }
162
163 memcpy((uint8_t *) mem->bo->cpu + mem->stack_bottom, data, sz);
164 mali_ptr gpu = mem->bo->gpu + mem->stack_bottom;
165
166 mem->stack_bottom += aligned_sz;
167 return gpu;
168 }