Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
5564 | serge | 1 | /************************************************************************** |
2 | * |
||
3 | * Copyright 2003 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 | |||
29 | #include "main/imports.h" |
||
30 | #include "main/mtypes.h" |
||
31 | #include "main/macros.h" |
||
32 | #include "main/bufferobj.h" |
||
33 | |||
34 | #include "intel_blit.h" |
||
35 | #include "intel_buffer_objects.h" |
||
36 | #include "intel_batchbuffer.h" |
||
37 | #include "intel_context.h" |
||
38 | #include "intel_fbo.h" |
||
39 | #include "intel_mipmap_tree.h" |
||
40 | #include "intel_regions.h" |
||
41 | |||
42 | static GLboolean |
||
43 | intel_bufferobj_unmap(struct gl_context * ctx, struct gl_buffer_object *obj, |
||
44 | gl_map_buffer_index index); |
||
45 | |||
46 | /** Allocates a new drm_intel_bo to store the data for the buffer object. */ |
||
47 | static void |
||
48 | intel_bufferobj_alloc_buffer(struct intel_context *intel, |
||
49 | struct intel_buffer_object *intel_obj) |
||
50 | { |
||
51 | intel_obj->buffer = drm_intel_bo_alloc(intel->bufmgr, "bufferobj", |
||
52 | intel_obj->Base.Size, 64); |
||
53 | } |
||
54 | |||
55 | static void |
||
56 | release_buffer(struct intel_buffer_object *intel_obj) |
||
57 | { |
||
58 | drm_intel_bo_unreference(intel_obj->buffer); |
||
59 | intel_obj->buffer = NULL; |
||
60 | intel_obj->offset = 0; |
||
61 | intel_obj->source = 0; |
||
62 | } |
||
63 | |||
64 | /** |
||
65 | * There is some duplication between mesa's bufferobjects and our |
||
66 | * bufmgr buffers. Both have an integer handle and a hashtable to |
||
67 | * lookup an opaque structure. It would be nice if the handles and |
||
68 | * internal structure where somehow shared. |
||
69 | */ |
||
70 | static struct gl_buffer_object * |
||
71 | intel_bufferobj_alloc(struct gl_context * ctx, GLuint name) |
||
72 | { |
||
73 | struct intel_buffer_object *obj = CALLOC_STRUCT(intel_buffer_object); |
||
74 | |||
75 | _mesa_initialize_buffer_object(ctx, &obj->Base, name); |
||
76 | |||
77 | obj->buffer = NULL; |
||
78 | |||
79 | return &obj->Base; |
||
80 | } |
||
81 | |||
82 | /** |
||
83 | * Deallocate/free a vertex/pixel buffer object. |
||
84 | * Called via glDeleteBuffersARB(). |
||
85 | */ |
||
86 | static void |
||
87 | intel_bufferobj_free(struct gl_context * ctx, struct gl_buffer_object *obj) |
||
88 | { |
||
89 | struct intel_buffer_object *intel_obj = intel_buffer_object(obj); |
||
90 | |||
91 | assert(intel_obj); |
||
92 | |||
93 | /* Buffer objects are automatically unmapped when deleting according |
||
94 | * to the spec, but Mesa doesn't do UnmapBuffer for us at context destroy |
||
95 | * (though it does if you call glDeleteBuffers) |
||
96 | */ |
||
97 | _mesa_buffer_unmap_all_mappings(ctx, obj); |
||
98 | |||
99 | _mesa_align_free(intel_obj->sys_buffer); |
||
100 | |||
101 | drm_intel_bo_unreference(intel_obj->buffer); |
||
102 | free(intel_obj); |
||
103 | } |
||
104 | |||
105 | |||
106 | |||
107 | /** |
||
108 | * Allocate space for and store data in a buffer object. Any data that was |
||
109 | * previously stored in the buffer object is lost. If data is NULL, |
||
110 | * memory will be allocated, but no copy will occur. |
||
111 | * Called via ctx->Driver.BufferData(). |
||
112 | * \return true for success, false if out of memory |
||
113 | */ |
||
114 | static GLboolean |
||
115 | intel_bufferobj_data(struct gl_context * ctx, |
||
116 | GLenum target, |
||
117 | GLsizeiptrARB size, |
||
118 | const GLvoid * data, |
||
119 | GLenum usage, |
||
120 | GLbitfield storageFlags, |
||
121 | struct gl_buffer_object *obj) |
||
122 | { |
||
123 | struct intel_context *intel = intel_context(ctx); |
||
124 | struct intel_buffer_object *intel_obj = intel_buffer_object(obj); |
||
125 | |||
126 | intel_obj->Base.Size = size; |
||
127 | intel_obj->Base.Usage = usage; |
||
128 | intel_obj->Base.StorageFlags = storageFlags; |
||
129 | |||
130 | assert(!obj->Mappings[MAP_USER].Pointer); /* Mesa should have unmapped it */ |
||
131 | assert(!obj->Mappings[MAP_INTERNAL].Pointer); |
||
132 | |||
133 | if (intel_obj->buffer != NULL) |
||
134 | release_buffer(intel_obj); |
||
135 | |||
136 | _mesa_align_free(intel_obj->sys_buffer); |
||
137 | intel_obj->sys_buffer = NULL; |
||
138 | |||
139 | if (size != 0) { |
||
140 | /* Stick VBOs in system memory, as we're always doing swtnl with their |
||
141 | * contents anyway. |
||
142 | */ |
||
143 | if (target == GL_ARRAY_BUFFER || target == GL_ELEMENT_ARRAY_BUFFER) { |
||
144 | intel_obj->sys_buffer = |
||
145 | _mesa_align_malloc(size, ctx->Const.MinMapBufferAlignment); |
||
146 | if (intel_obj->sys_buffer != NULL) { |
||
147 | if (data != NULL) |
||
148 | memcpy(intel_obj->sys_buffer, data, size); |
||
149 | return true; |
||
150 | } |
||
151 | } |
||
152 | |||
153 | intel_bufferobj_alloc_buffer(intel, intel_obj); |
||
154 | if (!intel_obj->buffer) |
||
155 | return false; |
||
156 | |||
157 | if (data != NULL) |
||
158 | drm_intel_bo_subdata(intel_obj->buffer, 0, size, data); |
||
159 | } |
||
160 | |||
161 | return true; |
||
162 | } |
||
163 | |||
164 | |||
165 | /** |
||
166 | * Replace data in a subrange of buffer object. If the data range |
||
167 | * specified by size + offset extends beyond the end of the buffer or |
||
168 | * if data is NULL, no copy is performed. |
||
169 | * Called via glBufferSubDataARB(). |
||
170 | */ |
||
171 | static void |
||
172 | intel_bufferobj_subdata(struct gl_context * ctx, |
||
173 | GLintptrARB offset, |
||
174 | GLsizeiptrARB size, |
||
175 | const GLvoid * data, struct gl_buffer_object *obj) |
||
176 | { |
||
177 | struct intel_context *intel = intel_context(ctx); |
||
178 | struct intel_buffer_object *intel_obj = intel_buffer_object(obj); |
||
179 | bool busy; |
||
180 | |||
181 | if (size == 0) |
||
182 | return; |
||
183 | |||
184 | assert(intel_obj); |
||
185 | |||
186 | /* If we have a single copy in system memory, update that */ |
||
187 | if (intel_obj->sys_buffer) { |
||
188 | if (intel_obj->source) |
||
189 | release_buffer(intel_obj); |
||
190 | |||
191 | if (intel_obj->buffer == NULL) { |
||
192 | memcpy((char *)intel_obj->sys_buffer + offset, data, size); |
||
193 | return; |
||
194 | } |
||
195 | |||
196 | _mesa_align_free(intel_obj->sys_buffer); |
||
197 | intel_obj->sys_buffer = NULL; |
||
198 | } |
||
199 | |||
200 | /* Otherwise we need to update the copy in video memory. */ |
||
201 | busy = |
||
202 | drm_intel_bo_busy(intel_obj->buffer) || |
||
203 | drm_intel_bo_references(intel->batch.bo, intel_obj->buffer); |
||
204 | |||
205 | if (busy) { |
||
206 | if (size == intel_obj->Base.Size) { |
||
207 | /* Replace the current busy bo with fresh data. */ |
||
208 | drm_intel_bo_unreference(intel_obj->buffer); |
||
209 | intel_bufferobj_alloc_buffer(intel, intel_obj); |
||
210 | drm_intel_bo_subdata(intel_obj->buffer, 0, size, data); |
||
211 | } else { |
||
212 | perf_debug("Using a blit copy to avoid stalling on %ldb " |
||
213 | "glBufferSubData() to a busy buffer object.\n", |
||
214 | (long)size); |
||
215 | drm_intel_bo *temp_bo = |
||
216 | drm_intel_bo_alloc(intel->bufmgr, "subdata temp", size, 64); |
||
217 | |||
218 | drm_intel_bo_subdata(temp_bo, 0, size, data); |
||
219 | |||
220 | intel_emit_linear_blit(intel, |
||
221 | intel_obj->buffer, offset, |
||
222 | temp_bo, 0, |
||
223 | size); |
||
224 | |||
225 | drm_intel_bo_unreference(temp_bo); |
||
226 | } |
||
227 | } else { |
||
228 | drm_intel_bo_subdata(intel_obj->buffer, offset, size, data); |
||
229 | } |
||
230 | } |
||
231 | |||
232 | |||
233 | /** |
||
234 | * Called via glGetBufferSubDataARB(). |
||
235 | */ |
||
236 | static void |
||
237 | intel_bufferobj_get_subdata(struct gl_context * ctx, |
||
238 | GLintptrARB offset, |
||
239 | GLsizeiptrARB size, |
||
240 | GLvoid * data, struct gl_buffer_object *obj) |
||
241 | { |
||
242 | struct intel_buffer_object *intel_obj = intel_buffer_object(obj); |
||
243 | struct intel_context *intel = intel_context(ctx); |
||
244 | |||
245 | assert(intel_obj); |
||
246 | if (intel_obj->sys_buffer) |
||
247 | memcpy(data, (char *)intel_obj->sys_buffer + offset, size); |
||
248 | else { |
||
249 | if (drm_intel_bo_references(intel->batch.bo, intel_obj->buffer)) { |
||
250 | intel_batchbuffer_flush(intel); |
||
251 | } |
||
252 | drm_intel_bo_get_subdata(intel_obj->buffer, offset, size, data); |
||
253 | } |
||
254 | } |
||
255 | |||
256 | |||
257 | |||
258 | /** |
||
259 | * Called via glMapBufferRange and glMapBuffer |
||
260 | * |
||
261 | * The goal of this extension is to allow apps to accumulate their rendering |
||
262 | * at the same time as they accumulate their buffer object. Without it, |
||
263 | * you'd end up blocking on execution of rendering every time you mapped |
||
264 | * the buffer to put new data in. |
||
265 | * |
||
266 | * We support it in 3 ways: If unsynchronized, then don't bother |
||
267 | * flushing the batchbuffer before mapping the buffer, which can save blocking |
||
268 | * in many cases. If we would still block, and they allow the whole buffer |
||
269 | * to be invalidated, then just allocate a new buffer to replace the old one. |
||
270 | * If not, and we'd block, and they allow the subrange of the buffer to be |
||
271 | * invalidated, then we can make a new little BO, let them write into that, |
||
272 | * and blit it into the real BO at unmap time. |
||
273 | */ |
||
274 | static void * |
||
275 | intel_bufferobj_map_range(struct gl_context * ctx, |
||
276 | GLintptr offset, GLsizeiptr length, |
||
277 | GLbitfield access, struct gl_buffer_object *obj, |
||
278 | gl_map_buffer_index index) |
||
279 | { |
||
280 | struct intel_context *intel = intel_context(ctx); |
||
281 | struct intel_buffer_object *intel_obj = intel_buffer_object(obj); |
||
282 | |||
283 | assert(intel_obj); |
||
284 | |||
285 | /* _mesa_MapBufferRange (GL entrypoint) sets these, but the vbo module also |
||
286 | * internally uses our functions directly. |
||
287 | */ |
||
288 | obj->Mappings[index].Offset = offset; |
||
289 | obj->Mappings[index].Length = length; |
||
290 | obj->Mappings[index].AccessFlags = access; |
||
291 | |||
292 | if (intel_obj->sys_buffer) { |
||
293 | const bool read_only = |
||
294 | (access & (GL_MAP_READ_BIT | GL_MAP_WRITE_BIT)) == GL_MAP_READ_BIT; |
||
295 | |||
296 | if (!read_only && intel_obj->source) |
||
297 | release_buffer(intel_obj); |
||
298 | |||
299 | if (!intel_obj->buffer || intel_obj->source) { |
||
300 | obj->Mappings[index].Pointer = intel_obj->sys_buffer + offset; |
||
301 | return obj->Mappings[index].Pointer; |
||
302 | } |
||
303 | |||
304 | _mesa_align_free(intel_obj->sys_buffer); |
||
305 | intel_obj->sys_buffer = NULL; |
||
306 | } |
||
307 | |||
308 | if (intel_obj->buffer == NULL) { |
||
309 | obj->Mappings[index].Pointer = NULL; |
||
310 | return NULL; |
||
311 | } |
||
312 | |||
313 | /* If the access is synchronized (like a normal buffer mapping), then get |
||
314 | * things flushed out so the later mapping syncs appropriately through GEM. |
||
315 | * If the user doesn't care about existing buffer contents and mapping would |
||
316 | * cause us to block, then throw out the old buffer. |
||
317 | * |
||
318 | * If they set INVALIDATE_BUFFER, we can pitch the current contents to |
||
319 | * achieve the required synchronization. |
||
320 | */ |
||
321 | if (!(access & GL_MAP_UNSYNCHRONIZED_BIT)) { |
||
322 | if (drm_intel_bo_references(intel->batch.bo, intel_obj->buffer)) { |
||
323 | if (access & GL_MAP_INVALIDATE_BUFFER_BIT) { |
||
324 | drm_intel_bo_unreference(intel_obj->buffer); |
||
325 | intel_bufferobj_alloc_buffer(intel, intel_obj); |
||
326 | } else { |
||
327 | perf_debug("Stalling on the GPU for mapping a busy buffer " |
||
328 | "object\n"); |
||
329 | intel_flush(ctx); |
||
330 | } |
||
331 | } else if (drm_intel_bo_busy(intel_obj->buffer) && |
||
332 | (access & GL_MAP_INVALIDATE_BUFFER_BIT)) { |
||
333 | drm_intel_bo_unreference(intel_obj->buffer); |
||
334 | intel_bufferobj_alloc_buffer(intel, intel_obj); |
||
335 | } |
||
336 | } |
||
337 | |||
338 | /* If the user is mapping a range of an active buffer object but |
||
339 | * doesn't require the current contents of that range, make a new |
||
340 | * BO, and we'll copy what they put in there out at unmap or |
||
341 | * FlushRange time. |
||
342 | */ |
||
343 | if ((access & GL_MAP_INVALIDATE_RANGE_BIT) && |
||
344 | drm_intel_bo_busy(intel_obj->buffer)) { |
||
345 | /* Ensure that the base alignment of the allocation meets the alignment |
||
346 | * guarantees the driver has advertised to the application. |
||
347 | */ |
||
348 | const unsigned alignment = ctx->Const.MinMapBufferAlignment; |
||
349 | const unsigned extra = (uintptr_t) offset % alignment; |
||
350 | |||
351 | if (access & GL_MAP_FLUSH_EXPLICIT_BIT) { |
||
352 | intel_obj->range_map_buffer[index] = |
||
353 | _mesa_align_malloc(length + extra, alignment); |
||
354 | obj->Mappings[index].Pointer = |
||
355 | intel_obj->range_map_buffer[index] + extra; |
||
356 | } else { |
||
357 | intel_obj->range_map_bo[index] = drm_intel_bo_alloc(intel->bufmgr, |
||
358 | "range map", |
||
359 | length + extra, |
||
360 | alignment); |
||
361 | if (!(access & GL_MAP_READ_BIT)) { |
||
362 | drm_intel_gem_bo_map_gtt(intel_obj->range_map_bo[index]); |
||
363 | } else { |
||
364 | drm_intel_bo_map(intel_obj->range_map_bo[index], |
||
365 | (access & GL_MAP_WRITE_BIT) != 0); |
||
366 | } |
||
367 | obj->Mappings[index].Pointer = |
||
368 | intel_obj->range_map_bo[index]->virtual + extra; |
||
369 | } |
||
370 | return obj->Mappings[index].Pointer; |
||
371 | } |
||
372 | |||
373 | if (access & GL_MAP_UNSYNCHRONIZED_BIT) |
||
374 | drm_intel_gem_bo_map_unsynchronized(intel_obj->buffer); |
||
375 | else if (!(access & GL_MAP_READ_BIT)) { |
||
376 | drm_intel_gem_bo_map_gtt(intel_obj->buffer); |
||
377 | } else { |
||
378 | drm_intel_bo_map(intel_obj->buffer, (access & GL_MAP_WRITE_BIT) != 0); |
||
379 | } |
||
380 | |||
381 | obj->Mappings[index].Pointer = intel_obj->buffer->virtual + offset; |
||
382 | return obj->Mappings[index].Pointer; |
||
383 | } |
||
384 | |||
385 | /* Ideally we'd use a BO to avoid taking up cache space for the temporary |
||
386 | * data, but FlushMappedBufferRange may be followed by further writes to |
||
387 | * the pointer, so we would have to re-map after emitting our blit, which |
||
388 | * would defeat the point. |
||
389 | */ |
||
390 | static void |
||
391 | intel_bufferobj_flush_mapped_range(struct gl_context *ctx, |
||
392 | GLintptr offset, GLsizeiptr length, |
||
393 | struct gl_buffer_object *obj, |
||
394 | gl_map_buffer_index index) |
||
395 | { |
||
396 | struct intel_context *intel = intel_context(ctx); |
||
397 | struct intel_buffer_object *intel_obj = intel_buffer_object(obj); |
||
398 | drm_intel_bo *temp_bo; |
||
399 | |||
400 | /* Unless we're in the range map using a temporary system buffer, |
||
401 | * there's no work to do. |
||
402 | */ |
||
403 | if (intel_obj->range_map_buffer[index] == NULL) |
||
404 | return; |
||
405 | |||
406 | if (length == 0) |
||
407 | return; |
||
408 | |||
409 | temp_bo = drm_intel_bo_alloc(intel->bufmgr, "range map flush", length, 64); |
||
410 | |||
411 | /* Use obj->Pointer instead of intel_obj->range_map_buffer because the |
||
412 | * former points to the actual mapping while the latter may be offset to |
||
413 | * meet alignment guarantees. |
||
414 | */ |
||
415 | drm_intel_bo_subdata(temp_bo, 0, length, obj->Mappings[index].Pointer); |
||
416 | |||
417 | intel_emit_linear_blit(intel, |
||
418 | intel_obj->buffer, |
||
419 | obj->Mappings[index].Offset + offset, |
||
420 | temp_bo, 0, |
||
421 | length); |
||
422 | |||
423 | drm_intel_bo_unreference(temp_bo); |
||
424 | } |
||
425 | |||
426 | |||
427 | /** |
||
428 | * Called via glUnmapBuffer(). |
||
429 | */ |
||
430 | static GLboolean |
||
431 | intel_bufferobj_unmap(struct gl_context * ctx, struct gl_buffer_object *obj, |
||
432 | gl_map_buffer_index index) |
||
433 | { |
||
434 | struct intel_context *intel = intel_context(ctx); |
||
435 | struct intel_buffer_object *intel_obj = intel_buffer_object(obj); |
||
436 | |||
437 | assert(intel_obj); |
||
438 | assert(obj->Mappings[index].Pointer); |
||
439 | if (intel_obj->sys_buffer != NULL) { |
||
440 | /* always keep the mapping around. */ |
||
441 | } else if (intel_obj->range_map_buffer[index] != NULL) { |
||
442 | /* Since we've emitted some blits to buffers that will (likely) be used |
||
443 | * in rendering operations in other cache domains in this batch, emit a |
||
444 | * flush. Once again, we wish for a domain tracker in libdrm to cover |
||
445 | * usage inside of a batchbuffer. |
||
446 | */ |
||
447 | intel_batchbuffer_emit_mi_flush(intel); |
||
448 | _mesa_align_free(intel_obj->range_map_buffer[index]); |
||
449 | intel_obj->range_map_buffer[index] = NULL; |
||
450 | } else if (intel_obj->range_map_bo[index] != NULL) { |
||
451 | const unsigned extra = obj->Mappings[index].Pointer - |
||
452 | intel_obj->range_map_bo[index]->virtual; |
||
453 | |||
454 | drm_intel_bo_unmap(intel_obj->range_map_bo[index]); |
||
455 | |||
456 | intel_emit_linear_blit(intel, |
||
457 | intel_obj->buffer, obj->Mappings[index].Offset, |
||
458 | intel_obj->range_map_bo[index], extra, |
||
459 | obj->Mappings[index].Length); |
||
460 | |||
461 | /* Since we've emitted some blits to buffers that will (likely) be used |
||
462 | * in rendering operations in other cache domains in this batch, emit a |
||
463 | * flush. Once again, we wish for a domain tracker in libdrm to cover |
||
464 | * usage inside of a batchbuffer. |
||
465 | */ |
||
466 | intel_batchbuffer_emit_mi_flush(intel); |
||
467 | |||
468 | drm_intel_bo_unreference(intel_obj->range_map_bo[index]); |
||
469 | intel_obj->range_map_bo[index] = NULL; |
||
470 | } else if (intel_obj->buffer != NULL) { |
||
471 | drm_intel_bo_unmap(intel_obj->buffer); |
||
472 | } |
||
473 | obj->Mappings[index].Pointer = NULL; |
||
474 | obj->Mappings[index].Offset = 0; |
||
475 | obj->Mappings[index].Length = 0; |
||
476 | |||
477 | return true; |
||
478 | } |
||
479 | |||
480 | drm_intel_bo * |
||
481 | intel_bufferobj_buffer(struct intel_context *intel, |
||
482 | struct intel_buffer_object *intel_obj) |
||
483 | { |
||
484 | if (intel_obj->source) |
||
485 | release_buffer(intel_obj); |
||
486 | |||
487 | if (intel_obj->buffer == NULL) { |
||
488 | intel_bufferobj_alloc_buffer(intel, intel_obj); |
||
489 | drm_intel_bo_subdata(intel_obj->buffer, |
||
490 | 0, intel_obj->Base.Size, |
||
491 | intel_obj->sys_buffer); |
||
492 | |||
493 | _mesa_align_free(intel_obj->sys_buffer); |
||
494 | intel_obj->sys_buffer = NULL; |
||
495 | intel_obj->offset = 0; |
||
496 | } |
||
497 | |||
498 | return intel_obj->buffer; |
||
499 | } |
||
500 | |||
501 | #define INTEL_UPLOAD_SIZE (64*1024) |
||
502 | |||
503 | void |
||
504 | intel_upload_finish(struct intel_context *intel) |
||
505 | { |
||
506 | if (!intel->upload.bo) |
||
507 | return; |
||
508 | |||
509 | if (intel->upload.buffer_len) { |
||
510 | drm_intel_bo_subdata(intel->upload.bo, |
||
511 | intel->upload.buffer_offset, |
||
512 | intel->upload.buffer_len, |
||
513 | intel->upload.buffer); |
||
514 | intel->upload.buffer_len = 0; |
||
515 | } |
||
516 | |||
517 | drm_intel_bo_unreference(intel->upload.bo); |
||
518 | intel->upload.bo = NULL; |
||
519 | } |
||
520 | |||
521 | static void wrap_buffers(struct intel_context *intel, GLuint size) |
||
522 | { |
||
523 | intel_upload_finish(intel); |
||
524 | |||
525 | if (size < INTEL_UPLOAD_SIZE) |
||
526 | size = INTEL_UPLOAD_SIZE; |
||
527 | |||
528 | intel->upload.bo = drm_intel_bo_alloc(intel->bufmgr, "upload", size, 0); |
||
529 | intel->upload.offset = 0; |
||
530 | } |
||
531 | |||
532 | void intel_upload_data(struct intel_context *intel, |
||
533 | const void *ptr, GLuint size, GLuint align, |
||
534 | drm_intel_bo **return_bo, |
||
535 | GLuint *return_offset) |
||
536 | { |
||
537 | GLuint base, delta; |
||
538 | |||
539 | base = (intel->upload.offset + align - 1) / align * align; |
||
540 | if (intel->upload.bo == NULL || base + size > intel->upload.bo->size) { |
||
541 | wrap_buffers(intel, size); |
||
542 | base = 0; |
||
543 | } |
||
544 | |||
545 | drm_intel_bo_reference(intel->upload.bo); |
||
546 | *return_bo = intel->upload.bo; |
||
547 | *return_offset = base; |
||
548 | |||
549 | delta = base - intel->upload.offset; |
||
550 | if (intel->upload.buffer_len && |
||
551 | intel->upload.buffer_len + delta + size > sizeof(intel->upload.buffer)) |
||
552 | { |
||
553 | drm_intel_bo_subdata(intel->upload.bo, |
||
554 | intel->upload.buffer_offset, |
||
555 | intel->upload.buffer_len, |
||
556 | intel->upload.buffer); |
||
557 | intel->upload.buffer_len = 0; |
||
558 | } |
||
559 | |||
560 | if (size < sizeof(intel->upload.buffer)) |
||
561 | { |
||
562 | if (intel->upload.buffer_len == 0) |
||
563 | intel->upload.buffer_offset = base; |
||
564 | else |
||
565 | intel->upload.buffer_len += delta; |
||
566 | |||
567 | memcpy(intel->upload.buffer + intel->upload.buffer_len, ptr, size); |
||
568 | intel->upload.buffer_len += size; |
||
569 | } |
||
570 | else |
||
571 | { |
||
572 | drm_intel_bo_subdata(intel->upload.bo, base, size, ptr); |
||
573 | } |
||
574 | |||
575 | intel->upload.offset = base + size; |
||
576 | } |
||
577 | |||
578 | drm_intel_bo * |
||
579 | intel_bufferobj_source(struct intel_context *intel, |
||
580 | struct intel_buffer_object *intel_obj, |
||
581 | GLuint align, GLuint *offset) |
||
582 | { |
||
583 | if (intel_obj->buffer == NULL) { |
||
584 | intel_upload_data(intel, |
||
585 | intel_obj->sys_buffer, intel_obj->Base.Size, align, |
||
586 | &intel_obj->buffer, &intel_obj->offset); |
||
587 | intel_obj->source = 1; |
||
588 | } |
||
589 | |||
590 | *offset = intel_obj->offset; |
||
591 | return intel_obj->buffer; |
||
592 | } |
||
593 | |||
594 | static void |
||
595 | intel_bufferobj_copy_subdata(struct gl_context *ctx, |
||
596 | struct gl_buffer_object *src, |
||
597 | struct gl_buffer_object *dst, |
||
598 | GLintptr read_offset, GLintptr write_offset, |
||
599 | GLsizeiptr size) |
||
600 | { |
||
601 | struct intel_context *intel = intel_context(ctx); |
||
602 | struct intel_buffer_object *intel_src = intel_buffer_object(src); |
||
603 | struct intel_buffer_object *intel_dst = intel_buffer_object(dst); |
||
604 | drm_intel_bo *src_bo, *dst_bo; |
||
605 | GLuint src_offset; |
||
606 | |||
607 | if (size == 0) |
||
608 | return; |
||
609 | |||
610 | /* If we're in system memory, just map and memcpy. */ |
||
611 | if (intel_src->sys_buffer || intel_dst->sys_buffer) { |
||
612 | /* The same buffer may be used, but note that regions copied may |
||
613 | * not overlap. |
||
614 | */ |
||
615 | if (src == dst) { |
||
616 | char *ptr = intel_bufferobj_map_range(ctx, 0, dst->Size, |
||
617 | GL_MAP_READ_BIT | |
||
618 | GL_MAP_WRITE_BIT, |
||
619 | dst, MAP_INTERNAL); |
||
620 | memmove(ptr + write_offset, ptr + read_offset, size); |
||
621 | intel_bufferobj_unmap(ctx, dst, MAP_INTERNAL); |
||
622 | } else { |
||
623 | const char *src_ptr; |
||
624 | char *dst_ptr; |
||
625 | |||
626 | src_ptr = intel_bufferobj_map_range(ctx, 0, src->Size, |
||
627 | GL_MAP_READ_BIT, src, |
||
628 | MAP_INTERNAL); |
||
629 | dst_ptr = intel_bufferobj_map_range(ctx, 0, dst->Size, |
||
630 | GL_MAP_WRITE_BIT, dst, |
||
631 | MAP_INTERNAL); |
||
632 | |||
633 | memcpy(dst_ptr + write_offset, src_ptr + read_offset, size); |
||
634 | |||
635 | intel_bufferobj_unmap(ctx, src, MAP_INTERNAL); |
||
636 | intel_bufferobj_unmap(ctx, dst, MAP_INTERNAL); |
||
637 | } |
||
638 | return; |
||
639 | } |
||
640 | |||
641 | /* Otherwise, we have real BOs, so blit them. */ |
||
642 | |||
643 | dst_bo = intel_bufferobj_buffer(intel, intel_dst); |
||
644 | src_bo = intel_bufferobj_source(intel, intel_src, 64, &src_offset); |
||
645 | |||
646 | intel_emit_linear_blit(intel, |
||
647 | dst_bo, write_offset, |
||
648 | src_bo, read_offset + src_offset, size); |
||
649 | |||
650 | /* Since we've emitted some blits to buffers that will (likely) be used |
||
651 | * in rendering operations in other cache domains in this batch, emit a |
||
652 | * flush. Once again, we wish for a domain tracker in libdrm to cover |
||
653 | * usage inside of a batchbuffer. |
||
654 | */ |
||
655 | intel_batchbuffer_emit_mi_flush(intel); |
||
656 | } |
||
657 | |||
658 | static GLenum |
||
659 | intel_buffer_purgeable(drm_intel_bo *buffer) |
||
660 | { |
||
661 | int retained = 0; |
||
662 | |||
663 | if (buffer != NULL) |
||
664 | retained = drm_intel_bo_madvise (buffer, I915_MADV_DONTNEED); |
||
665 | |||
666 | return retained ? GL_VOLATILE_APPLE : GL_RELEASED_APPLE; |
||
667 | } |
||
668 | |||
669 | static GLenum |
||
670 | intel_buffer_object_purgeable(struct gl_context * ctx, |
||
671 | struct gl_buffer_object *obj, |
||
672 | GLenum option) |
||
673 | { |
||
674 | struct intel_buffer_object *intel_obj = intel_buffer_object (obj); |
||
675 | |||
676 | if (intel_obj->buffer != NULL) |
||
677 | return intel_buffer_purgeable(intel_obj->buffer); |
||
678 | |||
679 | if (option == GL_RELEASED_APPLE) { |
||
680 | _mesa_align_free(intel_obj->sys_buffer); |
||
681 | intel_obj->sys_buffer = NULL; |
||
682 | |||
683 | return GL_RELEASED_APPLE; |
||
684 | } else { |
||
685 | /* XXX Create the buffer and madvise(MADV_DONTNEED)? */ |
||
686 | struct intel_context *intel = intel_context(ctx); |
||
687 | drm_intel_bo *bo = intel_bufferobj_buffer(intel, intel_obj); |
||
688 | |||
689 | return intel_buffer_purgeable(bo); |
||
690 | } |
||
691 | } |
||
692 | |||
693 | static GLenum |
||
694 | intel_texture_object_purgeable(struct gl_context * ctx, |
||
695 | struct gl_texture_object *obj, |
||
696 | GLenum option) |
||
697 | { |
||
698 | struct intel_texture_object *intel; |
||
699 | |||
700 | (void) ctx; |
||
701 | (void) option; |
||
702 | |||
703 | intel = intel_texture_object(obj); |
||
704 | if (intel->mt == NULL || intel->mt->region == NULL) |
||
705 | return GL_RELEASED_APPLE; |
||
706 | |||
707 | return intel_buffer_purgeable(intel->mt->region->bo); |
||
708 | } |
||
709 | |||
710 | static GLenum |
||
711 | intel_render_object_purgeable(struct gl_context * ctx, |
||
712 | struct gl_renderbuffer *obj, |
||
713 | GLenum option) |
||
714 | { |
||
715 | struct intel_renderbuffer *intel; |
||
716 | |||
717 | (void) ctx; |
||
718 | (void) option; |
||
719 | |||
720 | intel = intel_renderbuffer(obj); |
||
721 | if (intel->mt == NULL) |
||
722 | return GL_RELEASED_APPLE; |
||
723 | |||
724 | return intel_buffer_purgeable(intel->mt->region->bo); |
||
725 | } |
||
726 | |||
727 | static GLenum |
||
728 | intel_buffer_unpurgeable(drm_intel_bo *buffer) |
||
729 | { |
||
730 | int retained; |
||
731 | |||
732 | retained = 0; |
||
733 | if (buffer != NULL) |
||
734 | retained = drm_intel_bo_madvise (buffer, I915_MADV_WILLNEED); |
||
735 | |||
736 | return retained ? GL_RETAINED_APPLE : GL_UNDEFINED_APPLE; |
||
737 | } |
||
738 | |||
739 | static GLenum |
||
740 | intel_buffer_object_unpurgeable(struct gl_context * ctx, |
||
741 | struct gl_buffer_object *obj, |
||
742 | GLenum option) |
||
743 | { |
||
744 | (void) ctx; |
||
745 | (void) option; |
||
746 | |||
747 | return intel_buffer_unpurgeable(intel_buffer_object (obj)->buffer); |
||
748 | } |
||
749 | |||
750 | static GLenum |
||
751 | intel_texture_object_unpurgeable(struct gl_context * ctx, |
||
752 | struct gl_texture_object *obj, |
||
753 | GLenum option) |
||
754 | { |
||
755 | struct intel_texture_object *intel; |
||
756 | |||
757 | (void) ctx; |
||
758 | (void) option; |
||
759 | |||
760 | intel = intel_texture_object(obj); |
||
761 | if (intel->mt == NULL || intel->mt->region == NULL) |
||
762 | return GL_UNDEFINED_APPLE; |
||
763 | |||
764 | return intel_buffer_unpurgeable(intel->mt->region->bo); |
||
765 | } |
||
766 | |||
767 | static GLenum |
||
768 | intel_render_object_unpurgeable(struct gl_context * ctx, |
||
769 | struct gl_renderbuffer *obj, |
||
770 | GLenum option) |
||
771 | { |
||
772 | struct intel_renderbuffer *intel; |
||
773 | |||
774 | (void) ctx; |
||
775 | (void) option; |
||
776 | |||
777 | intel = intel_renderbuffer(obj); |
||
778 | if (intel->mt == NULL) |
||
779 | return GL_UNDEFINED_APPLE; |
||
780 | |||
781 | return intel_buffer_unpurgeable(intel->mt->region->bo); |
||
782 | } |
||
783 | |||
784 | void |
||
785 | intelInitBufferObjectFuncs(struct dd_function_table *functions) |
||
786 | { |
||
787 | functions->NewBufferObject = intel_bufferobj_alloc; |
||
788 | functions->DeleteBuffer = intel_bufferobj_free; |
||
789 | functions->BufferData = intel_bufferobj_data; |
||
790 | functions->BufferSubData = intel_bufferobj_subdata; |
||
791 | functions->GetBufferSubData = intel_bufferobj_get_subdata; |
||
792 | functions->MapBufferRange = intel_bufferobj_map_range; |
||
793 | functions->FlushMappedBufferRange = intel_bufferobj_flush_mapped_range; |
||
794 | functions->UnmapBuffer = intel_bufferobj_unmap; |
||
795 | functions->CopyBufferSubData = intel_bufferobj_copy_subdata; |
||
796 | |||
797 | functions->BufferObjectPurgeable = intel_buffer_object_purgeable; |
||
798 | functions->TextureObjectPurgeable = intel_texture_object_purgeable; |
||
799 | functions->RenderObjectPurgeable = intel_render_object_purgeable; |
||
800 | |||
801 | functions->BufferObjectUnpurgeable = intel_buffer_object_unpurgeable; |
||
802 | functions->TextureObjectUnpurgeable = intel_texture_object_unpurgeable; |
||
803 | functions->RenderObjectUnpurgeable = intel_render_object_unpurgeable; |
||
804 | }>> |