1/**************************************************************************
2 *
3 * Copyright 2009 VMware, Inc.
4 * All Rights Reserved.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the
8 * "Software"), to deal in the Software without restriction, including
9 * without limitation the rights to use, copy, modify, merge, publish,
10 * distribute, sub license, and/or sell copies of the Software, and to
11 * permit persons to whom the Software is furnished to do so, subject to
12 * the following conditions:
13 *
14 * The above copyright notice and this permission notice (including the
15 * next paragraph) shall be included in all copies or substantial portions
16 * of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
21 * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR
22 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
23 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
24 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 *
26 **************************************************************************/
27
28/* Helper utility for uploading user buffers & other data, and
29 * coalescing small buffers into larger ones.
30 */
31
32#include "pipe/p_defines.h"
33#include "util/u_inlines.h"
34#include "pipe/p_context.h"
35#include "util/u_memory.h"
36#include "util/u_math.h"
37
38#include "u_upload_mgr.h"
39
40
41struct u_upload_mgr {
42   struct pipe_context *pipe;
43
44   unsigned default_size;  /* Minimum size of the upload buffer, in bytes. */
45   unsigned bind;          /* Bitmask of PIPE_BIND_* flags. */
46   enum pipe_resource_usage usage;
47   unsigned flags;
48   unsigned map_flags;     /* Bitmask of PIPE_TRANSFER_* flags. */
49   boolean map_persistent; /* If persistent mappings are supported. */
50
51   struct pipe_resource *buffer;   /* Upload buffer. */
52   struct pipe_transfer *transfer; /* Transfer object for the upload buffer. */
53   uint8_t *map;    /* Pointer to the mapped upload buffer. */
54   unsigned offset; /* Aligned offset to the upload buffer, pointing
55                     * at the first unused byte. */
56   unsigned flushed_size; /* Size we have flushed by transfer_flush_region. */
57};
58
59
60struct u_upload_mgr *
61u_upload_create(struct pipe_context *pipe, unsigned default_size,
62                unsigned bind, enum pipe_resource_usage usage, unsigned flags)
63{
64   struct u_upload_mgr *upload = CALLOC_STRUCT(u_upload_mgr);
65   if (!upload)
66      return NULL;
67
68   upload->pipe = pipe;
69   upload->default_size = default_size;
70   upload->bind = bind;
71   upload->usage = usage;
72   upload->flags = flags;
73
74   upload->map_persistent =
75      pipe->screen->get_param(pipe->screen,
76                              PIPE_CAP_BUFFER_MAP_PERSISTENT_COHERENT);
77
78   if (upload->map_persistent) {
79      upload->map_flags = PIPE_TRANSFER_WRITE |
80                          PIPE_TRANSFER_UNSYNCHRONIZED |
81                          PIPE_TRANSFER_PERSISTENT |
82                          PIPE_TRANSFER_COHERENT;
83   }
84   else {
85      upload->map_flags = PIPE_TRANSFER_WRITE |
86                          PIPE_TRANSFER_UNSYNCHRONIZED |
87                          PIPE_TRANSFER_FLUSH_EXPLICIT;
88   }
89
90   return upload;
91}
92
93struct u_upload_mgr *
94u_upload_create_default(struct pipe_context *pipe)
95{
96   return u_upload_create(pipe, 1024 * 1024,
97                          PIPE_BIND_VERTEX_BUFFER |
98                          PIPE_BIND_INDEX_BUFFER |
99                          PIPE_BIND_CONSTANT_BUFFER,
100                          PIPE_USAGE_STREAM, 0);
101}
102
103struct u_upload_mgr *
104u_upload_clone(struct pipe_context *pipe, struct u_upload_mgr *upload)
105{
106   struct u_upload_mgr *result = u_upload_create(pipe, upload->default_size,
107                                                 upload->bind, upload->usage,
108                                                 upload->flags);
109   if (upload->map_persistent &&
110       upload->map_flags & PIPE_TRANSFER_FLUSH_EXPLICIT)
111      u_upload_enable_flush_explicit(result);
112
113   return result;
114}
115
116void
117u_upload_enable_flush_explicit(struct u_upload_mgr *upload)
118{
119   assert(upload->map_persistent);
120   upload->map_flags &= ~PIPE_TRANSFER_COHERENT;
121   upload->map_flags |= PIPE_TRANSFER_FLUSH_EXPLICIT;
122}
123
124static void
125upload_unmap_internal(struct u_upload_mgr *upload, boolean destroying)
126{
127   if (!upload->transfer)
128      return;
129
130   if (upload->map_flags & PIPE_TRANSFER_FLUSH_EXPLICIT) {
131      struct pipe_box *box = &upload->transfer->box;
132      unsigned flush_offset = box->x + upload->flushed_size;
133
134      if (upload->offset > flush_offset) {
135         pipe_buffer_flush_mapped_range(upload->pipe, upload->transfer,
136                                        flush_offset,
137                                        upload->offset - flush_offset);
138         upload->flushed_size = upload->offset;
139      }
140   }
141
142   if (destroying || !upload->map_persistent) {
143      pipe_transfer_unmap(upload->pipe, upload->transfer);
144      upload->transfer = NULL;
145      upload->map = NULL;
146      upload->flushed_size = 0;
147   }
148}
149
150
151void
152u_upload_unmap(struct u_upload_mgr *upload)
153{
154   upload_unmap_internal(upload, FALSE);
155}
156
157
158static void
159u_upload_release_buffer(struct u_upload_mgr *upload)
160{
161   /* Unmap and unreference the upload buffer. */
162   upload_unmap_internal(upload, TRUE);
163   pipe_resource_reference(&upload->buffer, NULL);
164}
165
166
167void
168u_upload_destroy(struct u_upload_mgr *upload)
169{
170   u_upload_release_buffer(upload);
171   FREE(upload);
172}
173
174
175static void
176u_upload_alloc_buffer(struct u_upload_mgr *upload, unsigned min_size)
177{
178   struct pipe_screen *screen = upload->pipe->screen;
179   struct pipe_resource buffer;
180   unsigned size;
181
182   /* Release the old buffer, if present:
183    */
184   u_upload_release_buffer(upload);
185
186   /* Allocate a new one:
187    */
188   size = align(MAX2(upload->default_size, min_size), 4096);
189
190   memset(&buffer, 0, sizeof buffer);
191   buffer.target = PIPE_BUFFER;
192   buffer.format = PIPE_FORMAT_R8_UNORM; /* want TYPELESS or similar */
193   buffer.bind = upload->bind;
194   buffer.usage = upload->usage;
195   buffer.flags = upload->flags;
196   buffer.width0 = size;
197   buffer.height0 = 1;
198   buffer.depth0 = 1;
199   buffer.array_size = 1;
200
201   if (upload->map_persistent) {
202      buffer.flags |= PIPE_RESOURCE_FLAG_MAP_PERSISTENT |
203                      PIPE_RESOURCE_FLAG_MAP_COHERENT;
204   }
205
206   upload->buffer = screen->resource_create(screen, &buffer);
207   if (upload->buffer == NULL)
208      return;
209
210   /* Map the new buffer. */
211   upload->map = pipe_buffer_map_range(upload->pipe, upload->buffer,
212                                       0, size, upload->map_flags,
213                                       &upload->transfer);
214   if (upload->map == NULL) {
215      upload->transfer = NULL;
216      pipe_resource_reference(&upload->buffer, NULL);
217      return;
218   }
219
220   upload->offset = 0;
221}
222
223void
224u_upload_alloc(struct u_upload_mgr *upload,
225               unsigned min_out_offset,
226               unsigned size,
227               unsigned alignment,
228               unsigned *out_offset,
229               struct pipe_resource **outbuf,
230               void **ptr)
231{
232   unsigned buffer_size = upload->buffer ? upload->buffer->width0 : 0;
233   unsigned offset;
234
235   min_out_offset = align(min_out_offset, alignment);
236
237   offset = align(upload->offset, alignment);
238   offset = MAX2(offset, min_out_offset);
239
240   /* Make sure we have enough space in the upload buffer
241    * for the sub-allocation.
242    */
243   if (unlikely(!upload->buffer || offset + size > buffer_size)) {
244      u_upload_alloc_buffer(upload, min_out_offset + size);
245
246      if (unlikely(!upload->buffer)) {
247         *out_offset = ~0;
248         pipe_resource_reference(outbuf, NULL);
249         *ptr = NULL;
250         return;
251      }
252
253      offset = min_out_offset;
254      buffer_size = upload->buffer->width0;
255   }
256
257   if (unlikely(!upload->map)) {
258      upload->map = pipe_buffer_map_range(upload->pipe, upload->buffer,
259                                          offset,
260                                          buffer_size - offset,
261                                          upload->map_flags,
262                                          &upload->transfer);
263      if (unlikely(!upload->map)) {
264         upload->transfer = NULL;
265         *out_offset = ~0;
266         pipe_resource_reference(outbuf, NULL);
267         *ptr = NULL;
268         return;
269      }
270
271      upload->map -= offset;
272   }
273
274   assert(offset < buffer_size);
275   assert(offset + size <= buffer_size);
276   assert(size);
277
278   /* Emit the return values: */
279   *ptr = upload->map + offset;
280   pipe_resource_reference(outbuf, upload->buffer);
281   *out_offset = offset;
282
283   upload->offset = offset + size;
284}
285
286void
287u_upload_data(struct u_upload_mgr *upload,
288              unsigned min_out_offset,
289              unsigned size,
290              unsigned alignment,
291              const void *data,
292              unsigned *out_offset,
293              struct pipe_resource **outbuf)
294{
295   uint8_t *ptr;
296
297   u_upload_alloc(upload, min_out_offset, size, alignment,
298                  out_offset, outbuf,
299                  (void**)&ptr);
300   if (ptr)
301      memcpy(ptr, data, size);
302}
303