u_upload_mgr.c revision 06286110b4fc0ff80ae21bb3d8ff9909db1f5d47
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;
45   unsigned alignment;
46   unsigned bind;
47
48   /* The active buffer:
49    */
50   struct pipe_resource *buffer;
51   struct pipe_transfer *transfer;
52   uint8_t *map;
53   unsigned size;
54   unsigned offset;
55};
56
57
58struct u_upload_mgr *u_upload_create( struct pipe_context *pipe,
59                                      unsigned default_size,
60                                      unsigned alignment,
61                                      unsigned bind )
62{
63   struct u_upload_mgr *upload = CALLOC_STRUCT( u_upload_mgr );
64   if (!upload)
65      return NULL;
66
67   upload->pipe = pipe;
68   upload->default_size = default_size;
69   upload->alignment = alignment;
70   upload->bind = bind;
71   upload->buffer = NULL;
72
73   return upload;
74}
75
76/* Release old buffer.
77 *
78 * This must usually be called prior to firing the command stream
79 * which references the upload buffer, as many memory managers will
80 * cause subsequent maps of a fired buffer to wait.
81 *
82 * Can improve this with a change to pipe_buffer_write to use the
83 * DONT_WAIT bit, but for now, it's easiest just to grab a new buffer.
84 */
85void u_upload_flush( struct u_upload_mgr *upload )
86{
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 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   upload->map = pipe_buffer_map(upload->pipe, upload->buffer,
124                                 PIPE_TRANSFER_WRITE, &upload->transfer);
125
126   upload->size = size;
127
128   upload->offset = 0;
129   return 0;
130
131fail:
132   if (upload->buffer)
133      pipe_resource_reference( &upload->buffer, NULL );
134
135   return PIPE_ERROR_OUT_OF_MEMORY;
136}
137
138enum pipe_error u_upload_alloc( struct u_upload_mgr *upload,
139                                unsigned min_out_offset,
140                                unsigned size,
141                                unsigned *out_offset,
142                                struct pipe_resource **outbuf,
143                                boolean *flushed,
144                                void **ptr )
145{
146   unsigned alloc_size = align( size, upload->alignment );
147   unsigned alloc_offset = align(min_out_offset, upload->alignment);
148   unsigned offset;
149
150   if (MAX2(upload->offset, alloc_offset) + alloc_size > upload->size) {
151      enum pipe_error ret = u_upload_alloc_buffer(upload,
152                                                  alloc_offset + alloc_size);
153      if (ret)
154         return ret;
155
156      *flushed = TRUE;
157   } else {
158      *flushed = FALSE;
159   }
160
161   offset = MAX2(upload->offset, alloc_offset);
162
163   assert(offset < upload->buffer->width0);
164   assert(offset + size <= upload->buffer->width0);
165   assert(size);
166
167   *ptr = upload->map + offset;
168
169   /* Emit the return values:
170    */
171   pipe_resource_reference( outbuf, upload->buffer );
172   *out_offset = offset;
173   upload->offset = offset + alloc_size;
174   return PIPE_OK;
175}
176
177enum pipe_error u_upload_data( struct u_upload_mgr *upload,
178                               unsigned min_out_offset,
179                               unsigned size,
180                               const void *data,
181                               unsigned *out_offset,
182                               struct pipe_resource **outbuf,
183                               boolean *flushed )
184{
185   uint8_t *ptr;
186   enum pipe_error ret = u_upload_alloc(upload, min_out_offset, size,
187                                        out_offset, outbuf, flushed,
188                                        (void**)&ptr);
189   if (ret)
190      return ret;
191
192   memcpy(ptr, data, size);
193   return PIPE_OK;
194}
195
196
197/* As above, but upload the full contents of a buffer.  Useful for
198 * uploading user buffers, avoids generating an explosion of GPU
199 * buffers if you have an app that does lots of small vertex buffer
200 * renders or DrawElements calls.
201 */
202enum pipe_error u_upload_buffer( struct u_upload_mgr *upload,
203                                 unsigned min_out_offset,
204                                 unsigned offset,
205                                 unsigned size,
206                                 struct pipe_resource *inbuf,
207                                 unsigned *out_offset,
208                                 struct pipe_resource **outbuf,
209                                 boolean *flushed )
210{
211   enum pipe_error ret = PIPE_OK;
212   struct pipe_transfer *transfer = NULL;
213   const char *map = NULL;
214
215   map = (const char *)pipe_buffer_map(upload->pipe,
216				       inbuf,
217				       PIPE_TRANSFER_READ,
218				       &transfer);
219
220   if (map == NULL) {
221      ret = PIPE_ERROR_OUT_OF_MEMORY;
222      goto done;
223   }
224
225   if (0)
226      debug_printf("upload ptr %p ofs %d sz %d\n", map, offset, size);
227
228   ret = u_upload_data( upload,
229                        min_out_offset,
230                        size,
231                        map + offset,
232                        out_offset,
233                        outbuf, flushed );
234
235done:
236   if (map)
237      pipe_buffer_unmap( upload->pipe, transfer );
238
239   return ret;
240}
241