u_upload_mgr.c revision 45b51a9e7021c004c754e1903afeb15fd885109e
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 alignment;     /* Alignment of each sub-allocation. */
46   unsigned bind;          /* Bitmask of PIPE_BIND_* flags. */
47
48   struct pipe_resource *buffer;   /* Upload buffer. */
49   struct pipe_transfer *transfer; /* Transfer object for the upload buffer. */
50   uint8_t *map;    /* Pointer to the mapped upload buffer. */
51   unsigned size;   /* Actual size of the upload buffer. */
52   unsigned offset; /* Aligned offset to the upload buffer, pointing
53                     * at the first unused byte. */
54};
55
56
57struct u_upload_mgr *u_upload_create( struct pipe_context *pipe,
58                                      unsigned default_size,
59                                      unsigned alignment,
60                                      unsigned bind )
61{
62   struct u_upload_mgr *upload = CALLOC_STRUCT( u_upload_mgr );
63   if (!upload)
64      return NULL;
65
66   upload->pipe = pipe;
67   upload->default_size = default_size;
68   upload->alignment = alignment;
69   upload->bind = bind;
70   upload->buffer = NULL;
71
72   return upload;
73}
74
75/* Release old buffer.
76 *
77 * This must usually be called prior to firing the command stream
78 * which references the upload buffer, as many memory managers will
79 * cause subsequent maps of a fired buffer to wait.
80 *
81 * Can improve this with a change to pipe_buffer_write to use the
82 * DONT_WAIT bit, but for now, it's easiest just to grab a new buffer.
83 */
84void u_upload_flush( struct u_upload_mgr *upload )
85{
86   /* Unmap and unreference the upload buffer. */
87   if (upload->transfer) {
88      pipe_transfer_unmap(upload->pipe, upload->transfer);
89      upload->transfer = NULL;
90   }
91   pipe_resource_reference( &upload->buffer, NULL );
92   upload->size = 0;
93}
94
95
96void u_upload_destroy( struct u_upload_mgr *upload )
97{
98   u_upload_flush( upload );
99   FREE( upload );
100}
101
102
103static enum pipe_error
104u_upload_alloc_buffer( struct u_upload_mgr *upload,
105                       unsigned min_size )
106{
107   unsigned size;
108
109   /* Release the old buffer, if present:
110    */
111   u_upload_flush( upload );
112
113   /* Allocate a new one:
114    */
115   size = align(MAX2(upload->default_size, min_size), 4096);
116
117   upload->buffer = pipe_buffer_create( upload->pipe->screen,
118                                        upload->bind,
119                                        size );
120   if (upload->buffer == NULL)
121      goto fail;
122
123   /* Map the new buffer. */
124   upload->map = pipe_buffer_map(upload->pipe, upload->buffer,
125                                 PIPE_TRANSFER_WRITE, &upload->transfer);
126
127   upload->size = size;
128
129   upload->offset = 0;
130   return 0;
131
132fail:
133   if (upload->buffer)
134      pipe_resource_reference( &upload->buffer, NULL );
135
136   return PIPE_ERROR_OUT_OF_MEMORY;
137}
138
139enum pipe_error u_upload_alloc( struct u_upload_mgr *upload,
140                                unsigned min_out_offset,
141                                unsigned size,
142                                unsigned *out_offset,
143                                struct pipe_resource **outbuf,
144                                boolean *flushed,
145                                void **ptr )
146{
147   unsigned alloc_size = align( size, upload->alignment );
148   unsigned alloc_offset = align(min_out_offset, upload->alignment);
149   unsigned offset;
150
151   /* Make sure we have enough space in the upload buffer
152    * for the sub-allocation. */
153   if (MAX2(upload->offset, alloc_offset) + alloc_size > upload->size) {
154      enum pipe_error ret = u_upload_alloc_buffer(upload,
155                                                  alloc_offset + alloc_size);
156      if (ret)
157         return ret;
158
159      *flushed = TRUE;
160   } else {
161      *flushed = FALSE;
162   }
163
164   offset = MAX2(upload->offset, alloc_offset);
165
166   assert(offset < upload->buffer->width0);
167   assert(offset + size <= upload->buffer->width0);
168   assert(size);
169
170   /* Emit the return values: */
171   *ptr = upload->map + offset;
172   pipe_resource_reference( outbuf, upload->buffer );
173   *out_offset = offset;
174
175   upload->offset = offset + alloc_size;
176   return PIPE_OK;
177}
178
179enum pipe_error u_upload_data( struct u_upload_mgr *upload,
180                               unsigned min_out_offset,
181                               unsigned size,
182                               const void *data,
183                               unsigned *out_offset,
184                               struct pipe_resource **outbuf,
185                               boolean *flushed )
186{
187   uint8_t *ptr;
188   enum pipe_error ret = u_upload_alloc(upload, min_out_offset, size,
189                                        out_offset, outbuf, flushed,
190                                        (void**)&ptr);
191   if (ret)
192      return ret;
193
194   memcpy(ptr, data, size);
195   return PIPE_OK;
196}
197
198
199/* As above, but upload the full contents of a buffer.  Useful for
200 * uploading user buffers, avoids generating an explosion of GPU
201 * buffers if you have an app that does lots of small vertex buffer
202 * renders or DrawElements calls.
203 */
204enum pipe_error u_upload_buffer( struct u_upload_mgr *upload,
205                                 unsigned min_out_offset,
206                                 unsigned offset,
207                                 unsigned size,
208                                 struct pipe_resource *inbuf,
209                                 unsigned *out_offset,
210                                 struct pipe_resource **outbuf,
211                                 boolean *flushed )
212{
213   enum pipe_error ret = PIPE_OK;
214   struct pipe_transfer *transfer = NULL;
215   const char *map = NULL;
216
217   map = (const char *)pipe_buffer_map(upload->pipe,
218				       inbuf,
219				       PIPE_TRANSFER_READ,
220				       &transfer);
221
222   if (map == NULL) {
223      ret = PIPE_ERROR_OUT_OF_MEMORY;
224      goto done;
225   }
226
227   if (0)
228      debug_printf("upload ptr %p ofs %d sz %d\n", map, offset, size);
229
230   ret = u_upload_data( upload,
231                        min_out_offset,
232                        size,
233                        map + offset,
234                        out_offset,
235                        outbuf, flushed );
236
237done:
238   if (map)
239      pipe_buffer_unmap( upload->pipe, transfer );
240
241   return ret;
242}
243