1 /*
2 *
3 * Copyright 2009 VMware, Inc.
4 * Copyright 2016 Axel Davy <axel.davy@ens.fr>
5 * All Rights Reserved.
6 *
7 * SPDX-License-Identifier: MIT
8 */
9
10 /* Adapted from u_upload_mgr.
11 * Makes suballocations from bigger allocations,
12 * while enabling fast mapping. */
13
14 #include "pipe/p_defines.h"
15 #include "util/u_inlines.h"
16 #include "pipe/p_context.h"
17 #include "util/u_memory.h"
18 #include "util/u_math.h"
19 #include "util/slab.h"
20
21 #include "nine_buffer_upload.h"
22
23 #include "nine_debug.h"
24
25 #define DBG_CHANNEL (DBG_INDEXBUFFER|DBG_VERTEXBUFFER)
26
27 struct nine_buffer_group {
28 unsigned refcount; /* How many sub-buffers live inside the buffer */
29 struct pipe_resource *resource;
30 struct pipe_transfer *transfer;
31 uint8_t *map;
32 unsigned free_offset; /* Aligned offset to the upload buffer, pointing
33 * at the first unused byte. */
34 };
35
36 struct nine_subbuffer {
37 struct nine_buffer_group *parent; /* Can be NULL */
38 struct pipe_resource *resource; /* The parent resource if apply */
39 unsigned offset; /* Offset inside the resource */
40 /* If there is no parent, the resource map. Else NULL. */
41 struct pipe_transfer *transfer;
42 uint8_t *map;
43 };
44
45 struct nine_buffer_upload {
46 struct pipe_context *pipe;
47 struct slab_mempool buffer_pool;
48
49 unsigned buffers_size; /* Size of the big allocated buffers */
50 unsigned num_buffers;
51 struct nine_buffer_group *buffers;
52 };
53
54 static void
nine_upload_create_buffer_group(struct nine_buffer_upload * upload,struct nine_buffer_group * group)55 nine_upload_create_buffer_group(struct nine_buffer_upload *upload,
56 struct nine_buffer_group *group)
57 {
58 struct pipe_resource resource;
59 struct pipe_screen *screen = upload->pipe->screen;
60 DBG("Allocating %p %p\n", upload, group);
61
62 memset(&resource, 0, sizeof(resource));
63 resource.target = PIPE_BUFFER;
64 resource.format = PIPE_FORMAT_R8_UNORM;
65 resource.bind = PIPE_BIND_VERTEX_BUFFER;
66 resource.usage = PIPE_USAGE_STREAM;
67 resource.width0 = upload->buffers_size;
68 resource.height0 = 1;
69 resource.depth0 = 1;
70 resource.array_size = 1;
71 resource.flags = PIPE_RESOURCE_FLAG_MAP_PERSISTENT |
72 PIPE_RESOURCE_FLAG_MAP_COHERENT;
73
74 group->refcount = 0;
75 group->resource = screen->resource_create(screen, &resource);
76 if (group->resource == NULL)
77 return;
78
79 group->map = pipe_buffer_map_range(upload->pipe, group->resource,
80 0, upload->buffers_size,
81 PIPE_MAP_WRITE |
82 #if DETECT_ARCH_X86
83 PIPE_MAP_ONCE |
84 #endif
85 PIPE_MAP_PERSISTENT |
86 PIPE_MAP_COHERENT,
87 &group->transfer);
88 if (group->map == NULL) {
89 group->transfer = NULL;
90 pipe_resource_reference(&group->resource, NULL);
91 return;
92 }
93
94 group->free_offset = 0;
95 DBG("Success: %p %p\n", group->map, group->map+upload->buffers_size);
96 }
97
98 static void
nine_upload_destroy_buffer_group(struct nine_buffer_upload * upload,struct nine_buffer_group * group)99 nine_upload_destroy_buffer_group(struct nine_buffer_upload *upload,
100 struct nine_buffer_group *group)
101 {
102 DBG("%p %p\n", upload, group);
103 DBG("Release: %p %p\n", group->map, group->map+upload->buffers_size);
104 assert(group->refcount == 0);
105
106 if (group->transfer)
107 pipe_buffer_unmap(upload->pipe, group->transfer);
108 if (group->resource)
109 pipe_resource_reference(&group->resource, NULL);
110 group->transfer = NULL;
111 group->map = NULL;
112 }
113
114 struct nine_buffer_upload *
nine_upload_create(struct pipe_context * pipe,unsigned buffers_size,unsigned num_buffers)115 nine_upload_create(struct pipe_context *pipe, unsigned buffers_size,
116 unsigned num_buffers)
117 {
118 struct nine_buffer_upload *upload;
119 int i;
120
121 DBG("\n");
122
123 if (!pipe->screen->caps.buffer_map_persistent_coherent)
124 return NULL;
125
126 upload = CALLOC_STRUCT(nine_buffer_upload);
127
128 if (!upload)
129 return NULL;
130
131 slab_create(&upload->buffer_pool, sizeof(struct nine_subbuffer), 4096);
132
133 upload->pipe = pipe;
134 upload->buffers_size = align(buffers_size, 4096);
135 upload->num_buffers = num_buffers;
136
137 upload->buffers = CALLOC(num_buffers, sizeof(struct nine_buffer_group));
138 if (!upload->buffers)
139 goto buffers_fail;
140
141 for (i = 0; i < num_buffers; i++)
142 nine_upload_create_buffer_group(upload, &upload->buffers[i]);
143
144 return upload;
145
146 buffers_fail:
147 slab_destroy(&upload->buffer_pool);
148 FREE(upload);
149 return NULL;
150 }
151
152 void
nine_upload_destroy(struct nine_buffer_upload * upload)153 nine_upload_destroy(struct nine_buffer_upload *upload)
154 {
155 int i;
156
157 DBG("%p\n", upload);
158
159 for (i = 0; i < upload->num_buffers; i++)
160 nine_upload_destroy_buffer_group(upload, &upload->buffers[i]);
161 slab_destroy(&upload->buffer_pool);
162 FREE(upload);
163 }
164
165 struct nine_subbuffer *
nine_upload_create_buffer(struct nine_buffer_upload * upload,unsigned buffer_size)166 nine_upload_create_buffer(struct nine_buffer_upload *upload,
167 unsigned buffer_size)
168 {
169 struct nine_subbuffer *buf = slab_alloc_st(&upload->buffer_pool);
170 struct nine_buffer_group *group = NULL;
171 unsigned size = align(buffer_size, 4096);
172 int i = 0;
173
174 DBG("%p %d\n", upload, buffer_size);
175
176 if (!buf)
177 return NULL;
178
179 for (i = 0; i < upload->num_buffers; i++) {
180 group = &upload->buffers[i];
181 if (group->resource &&
182 group->free_offset + size <= upload->buffers_size)
183 break;
184 }
185
186 if (i == upload->num_buffers) {
187 /* Allocate lonely buffer */
188 struct pipe_resource resource;
189 struct pipe_screen *screen = upload->pipe->screen;
190
191 DBG("Allocating buffer\n");
192 buf->parent = NULL;
193
194 memset(&resource, 0, sizeof(resource));
195 resource.target = PIPE_BUFFER;
196 resource.format = PIPE_FORMAT_R8_UNORM;
197 resource.bind = PIPE_BIND_VERTEX_BUFFER;
198 resource.usage = PIPE_USAGE_STREAM;
199 resource.width0 = buffer_size;
200 resource.height0 = 1;
201 resource.depth0 = 1;
202 resource.array_size = 1;
203 resource.flags = PIPE_RESOURCE_FLAG_MAP_PERSISTENT |
204 PIPE_RESOURCE_FLAG_MAP_COHERENT;
205
206 buf->resource = screen->resource_create(screen, &resource);
207 if (buf->resource == NULL) {
208 slab_free_st(&upload->buffer_pool, buf);
209 return NULL;
210 }
211
212 buf->map = pipe_buffer_map_range(upload->pipe, buf->resource,
213 0, buffer_size,
214 PIPE_MAP_WRITE |
215 #if DETECT_ARCH_X86
216 PIPE_MAP_ONCE |
217 #endif
218 PIPE_MAP_PERSISTENT |
219 PIPE_MAP_COHERENT,
220 &buf->transfer);
221 if (buf->map == NULL) {
222 pipe_resource_reference(&buf->resource, NULL);
223 slab_free_st(&upload->buffer_pool, buf);
224 return NULL;
225 }
226 buf->offset = 0;
227 return buf;
228 }
229
230 DBG("Using buffer group %d\n", i);
231
232 buf->parent = group;
233 buf->resource = NULL;
234 pipe_resource_reference(&buf->resource, group->resource);
235 buf->offset = group->free_offset;
236
237 group->free_offset += size;
238 group->refcount += 1;
239
240 return buf;
241 }
242
243 void
nine_upload_release_buffer(struct nine_buffer_upload * upload,struct nine_subbuffer * buf)244 nine_upload_release_buffer(struct nine_buffer_upload *upload,
245 struct nine_subbuffer *buf)
246 {
247 DBG("%p %p %p\n", upload, buf, buf->parent);
248
249 if (buf->parent) {
250 pipe_resource_reference(&buf->resource, NULL);
251 buf->parent->refcount--;
252 if (buf->parent->refcount == 0) {
253 /* Allocate new buffer */
254 nine_upload_destroy_buffer_group(upload, buf->parent);
255 nine_upload_create_buffer_group(upload, buf->parent);
256 }
257 } else {
258 /* lonely buffer */
259 if (buf->transfer)
260 pipe_buffer_unmap(upload->pipe, buf->transfer);
261 pipe_resource_reference(&buf->resource, NULL);
262 }
263
264 slab_free_st(&upload->buffer_pool, buf);
265 }
266
267 uint8_t *
nine_upload_buffer_get_map(struct nine_subbuffer * buf)268 nine_upload_buffer_get_map(struct nine_subbuffer *buf)
269 {
270 if (buf->parent) {
271 DBG("%d\n", buf->parent->refcount);
272 return buf->parent->map + buf->offset;
273 }
274 /* lonely buffer */
275 return buf->map;
276 }
277
278 struct pipe_resource *
nine_upload_buffer_resource_and_offset(struct nine_subbuffer * buf,unsigned * offset)279 nine_upload_buffer_resource_and_offset(struct nine_subbuffer *buf,
280 unsigned *offset)
281 {
282 *offset = buf->offset;
283 return buf->resource;
284 }
285