u_upload_mgr.c revision 37246f854b49101b5a112cd5a0bd93bab1b78f7a
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 "pipe/p_inlines.h"
34#include "pipe/p_screen.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_screen *screen;
43
44   unsigned default_size;
45   unsigned alignment;
46   unsigned usage;
47
48   /* The active buffer:
49    */
50   struct pipe_buffer *buffer;
51   unsigned size;
52   unsigned offset;
53};
54
55
56struct u_upload_mgr *u_upload_create( struct pipe_screen *screen,
57                                      unsigned default_size,
58                                      unsigned alignment,
59                                      unsigned usage )
60{
61   struct u_upload_mgr *upload = CALLOC_STRUCT( u_upload_mgr );
62
63   upload->default_size = default_size;
64   upload->screen = screen;
65   upload->alignment = alignment;
66   upload->usage = usage;
67   upload->buffer = NULL;
68
69   return upload;
70}
71
72
73static INLINE enum pipe_error
74my_buffer_write(struct pipe_screen *screen,
75                struct pipe_buffer *buf,
76                unsigned offset, unsigned size, unsigned dirty_size,
77                const void *data)
78{
79   uint8_t *map;
80
81   assert(offset < buf->size);
82   assert(offset + size <= buf->size);
83   assert(dirty_size >= size);
84   assert(size);
85
86   map = pipe_buffer_map_range(screen, buf, offset, size,
87                               PIPE_BUFFER_USAGE_CPU_WRITE |
88                               PIPE_BUFFER_USAGE_FLUSH_EXPLICIT |
89                               PIPE_BUFFER_USAGE_DISCARD |
90                               PIPE_BUFFER_USAGE_UNSYNCHRONIZED);
91   if (map == NULL)
92      return PIPE_ERROR_OUT_OF_MEMORY;
93
94   memcpy(map + offset, data, size);
95   pipe_buffer_flush_mapped_range(screen, buf, offset, dirty_size);
96   pipe_buffer_unmap(screen, buf);
97
98   return PIPE_OK;
99}
100
101/* Release old buffer.
102 *
103 * This must usually be called prior to firing the command stream
104 * which references the upload buffer, as many memory managers will
105 * cause subsequent maps of a fired buffer to wait.
106 *
107 * Can improve this with a change to pipe_buffer_write to use the
108 * DONT_WAIT bit, but for now, it's easiest just to grab a new buffer.
109 */
110void u_upload_flush( struct u_upload_mgr *upload )
111{
112   pipe_buffer_reference( &upload->buffer, NULL );
113   upload->size = 0;
114}
115
116
117void u_upload_destroy( struct u_upload_mgr *upload )
118{
119   u_upload_flush( upload );
120   FREE( upload );
121}
122
123
124static enum pipe_error
125u_upload_alloc_buffer( struct u_upload_mgr *upload,
126                       unsigned min_size )
127{
128   unsigned size;
129
130   /* Release old buffer, if present:
131    */
132   u_upload_flush( upload );
133
134   /* Allocate a new one:
135    */
136   size = align(MAX2(upload->default_size, min_size), 4096);
137
138   upload->buffer = pipe_buffer_create( upload->screen,
139                                        upload->alignment,
140                                        upload->usage | PIPE_BUFFER_USAGE_CPU_WRITE,
141                                        size );
142   if (upload->buffer == NULL)
143      goto fail;
144
145   upload->size = size;
146
147   upload->offset = 0;
148   return 0;
149
150fail:
151   if (upload->buffer)
152      pipe_buffer_reference( &upload->buffer, NULL );
153
154   return PIPE_ERROR_OUT_OF_MEMORY;
155}
156
157
158enum pipe_error u_upload_data( struct u_upload_mgr *upload,
159                               unsigned size,
160                               const void *data,
161                               unsigned *out_offset,
162                               struct pipe_buffer **outbuf )
163{
164   unsigned alloc_size = align( size, upload->alignment );
165   enum pipe_error ret = PIPE_OK;
166
167   if (upload->offset + alloc_size > upload->size) {
168      ret = u_upload_alloc_buffer( upload, alloc_size );
169      if (ret)
170         return ret;
171   }
172
173   /* Copy the data, using map_range if available:
174    */
175   ret = my_buffer_write( upload->screen,
176                          upload->buffer,
177                          upload->offset,
178                          size,
179                          alloc_size,
180                          data );
181   if (ret)
182      return ret;
183
184   /* Emit the return values:
185    */
186   pipe_buffer_reference( outbuf, upload->buffer );
187   *out_offset = upload->offset;
188   upload->offset += alloc_size;
189   return PIPE_OK;
190}
191
192
193/* As above, but upload the full contents of a buffer.  Useful for
194 * uploading user buffers, avoids generating an explosion of GPU
195 * buffers if you have an app that does lots of small vertex buffer
196 * renders or DrawElements calls.
197 */
198enum pipe_error u_upload_buffer( struct u_upload_mgr *upload,
199                                 unsigned offset,
200                                 unsigned size,
201                                 struct pipe_buffer *inbuf,
202                                 unsigned *out_offset,
203                                 struct pipe_buffer **outbuf )
204{
205   enum pipe_error ret = PIPE_OK;
206   const char *map = NULL;
207
208   map = (const char *)pipe_buffer_map(
209      upload->screen, inbuf, PIPE_BUFFER_USAGE_CPU_READ );
210
211   if (map == NULL) {
212      ret = PIPE_ERROR_OUT_OF_MEMORY;
213      goto done;
214   }
215
216   if (0)
217      debug_printf("upload ptr %p ofs %d sz %d\n", map, offset, size);
218
219   ret = u_upload_data( upload,
220                        size,
221                        map + offset,
222                        out_offset,
223                        outbuf );
224   if (ret)
225      goto done;
226
227done:
228   if (map)
229      pipe_buffer_unmap( upload->screen, inbuf );
230
231   return ret;
232}
233