u_upload_mgr.c revision 18a6f0f1a7fd509cebdc364d67b9476df1d33917
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_error.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   if (map == NULL)
90      return PIPE_ERROR_OUT_OF_MEMORY;
91
92   memcpy(map + offset, data, size);
93   pipe_buffer_flush_mapped_range(screen, buf, offset, dirty_size);
94   pipe_buffer_unmap(screen, buf);
95
96   return PIPE_OK;
97}
98
99/* Release old buffer.
100 *
101 * This must usually be called prior to firing the command stream
102 * which references the upload buffer, as many memory managers will
103 * cause subsequent maps of a fired buffer to wait.
104 *
105 * Can improve this with a change to pipe_buffer_write to use the
106 * DONT_WAIT bit, but for now, it's easiest just to grab a new buffer.
107 */
108void u_upload_flush( struct u_upload_mgr *upload )
109{
110   pipe_buffer_reference( &upload->buffer, NULL );
111   upload->size = 0;
112}
113
114
115void u_upload_destroy( struct u_upload_mgr *upload )
116{
117   u_upload_flush( upload );
118   FREE( upload );
119}
120
121
122static enum pipe_error
123u_upload_alloc_buffer( struct u_upload_mgr *upload,
124                       unsigned min_size )
125{
126   /* Release old buffer, if present:
127    */
128   u_upload_flush( upload );
129
130   /* Allocate a new one:
131    */
132   upload->size = align(MAX2(upload->default_size, min_size), 4096);
133
134   upload->buffer = pipe_buffer_create( upload->screen,
135                                        upload->alignment,
136                                        upload->usage | PIPE_BUFFER_USAGE_CPU_WRITE,
137                                        upload->size );
138   if (upload->buffer == NULL)
139      goto fail;
140
141   upload->offset = 0;
142   return 0;
143
144fail:
145   if (upload->buffer)
146      pipe_buffer_reference( &upload->buffer, NULL );
147
148   return PIPE_ERROR_OUT_OF_MEMORY;
149}
150
151
152enum pipe_error u_upload_data( struct u_upload_mgr *upload,
153                               unsigned size,
154                               const void *data,
155                               unsigned *out_offset,
156                               struct pipe_buffer **outbuf )
157{
158   unsigned alloc_size = align( size, upload->alignment );
159   enum pipe_error ret = PIPE_OK;
160
161   if (upload->offset + alloc_size > upload->size) {
162      ret = u_upload_alloc_buffer( upload, alloc_size );
163      if (ret)
164         return ret;
165   }
166
167   /* Copy the data, using map_range if available:
168    */
169   ret = my_buffer_write( upload->screen,
170                          upload->buffer,
171                          upload->offset,
172                          size,
173                          alloc_size,
174                          data );
175   if (ret)
176      return ret;
177
178   /* Emit the return values:
179    */
180   pipe_buffer_reference( outbuf, upload->buffer );
181   *out_offset = upload->offset;
182   upload->offset += alloc_size;
183   return PIPE_OK;
184}
185
186
187/* As above, but upload the full contents of a buffer.  Useful for
188 * uploading user buffers, avoids generating an explosion of GPU
189 * buffers if you have an app that does lots of small vertex buffer
190 * renders or DrawElements calls.
191 */
192enum pipe_error u_upload_buffer( struct u_upload_mgr *upload,
193                                 unsigned offset,
194                                 unsigned size,
195                                 struct pipe_buffer *inbuf,
196                                 unsigned *out_offset,
197                                 struct pipe_buffer **outbuf )
198{
199   enum pipe_error ret = PIPE_OK;
200   const char *map = NULL;
201
202   map = (const char *)pipe_buffer_map(
203      upload->screen, inbuf, PIPE_BUFFER_USAGE_CPU_READ );
204
205   if (map == NULL) {
206      ret = PIPE_ERROR_OUT_OF_MEMORY;
207      goto done;
208   }
209
210   if (0)
211      debug_printf("upload ptr %p ofs %d sz %d\n", map, offset, size);
212
213   ret = u_upload_data( upload,
214                        size,
215                        map + offset,
216                        out_offset,
217                        outbuf );
218   if (ret)
219      goto done;
220
221done:
222   if (map)
223      pipe_buffer_unmap( upload->screen, inbuf );
224
225   return ret;
226}
227