u_upload_mgr: new features
[mesa.git] / src / gallium / auxiliary / util / u_upload_mgr.c
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
41 struct 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
58 struct 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 */
85 void 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
96 void u_upload_destroy( struct u_upload_mgr *upload )
97 {
98 u_upload_flush( upload );
99 FREE( upload );
100 }
101
102
103 static enum pipe_error
104 u_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
131 fail:
132 if (upload->buffer)
133 pipe_resource_reference( &upload->buffer, NULL );
134
135 return PIPE_ERROR_OUT_OF_MEMORY;
136 }
137
138 enum 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
177 enum 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 */
202 enum 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
235 done:
236 if (map)
237 pipe_buffer_unmap( upload->pipe, transfer );
238
239 return ret;
240 }