Merge branch 'master' of git+ssh://joukj@git.freedesktop.org/git/mesa/mesa
[mesa.git] / src / mesa / drivers / dri / nouveau / nouveau_bufferobj.c
1 #include "bufferobj.h"
2 #include "enums.h"
3
4 #include "nouveau_bufferobj.h"
5 #include "nouveau_buffers.h"
6 #include "nouveau_context.h"
7 #include "nouveau_drm.h"
8 #include "nouveau_object.h"
9 #include "nouveau_msg.h"
10
11 #define NOUVEAU_MEM_FREE(mem) do { \
12 nouveau_mem_free(ctx, (mem)); \
13 (mem) = NULL; \
14 } while(0)
15
16 #define DEBUG(fmt,args...) do { \
17 if (NOUVEAU_DEBUG & DEBUG_BUFFEROBJ) { \
18 fprintf(stderr, "%s: "fmt, __func__, ##args); \
19 } \
20 } while(0)
21
22 static GLboolean
23 nouveau_bo_download_from_screen(GLcontext *ctx, GLuint offset, GLuint size,
24 struct gl_buffer_object *bo)
25 {
26 nouveauContextPtr nmesa = NOUVEAU_CONTEXT(ctx);
27 nouveau_buffer_object *nbo = (nouveau_buffer_object *)bo;
28 nouveau_mem *in_mem;
29
30 DEBUG("bo=%p, offset=%d, size=%d\n", bo, offset, size);
31
32 /* If there's a permanent backing store, blit directly into it */
33 if (nbo->cpu_mem) {
34 if (nbo->cpu_mem != nbo->gpu_mem) {
35 DEBUG("..cpu_mem\n");
36 nouveau_memformat_flat_emit(ctx, nbo->cpu_mem,
37 nbo->gpu_mem,
38 offset, offset, size);
39 }
40 } else {
41 DEBUG("..sys_mem\n");
42 in_mem = nouveau_mem_alloc(ctx, NOUVEAU_MEM_AGP, size, 0);
43 if (in_mem) {
44 DEBUG("....via GART\n");
45 /* otherwise, try blitting to faster memory and
46 * copying from there
47 */
48 nouveau_memformat_flat_emit(ctx, in_mem, nbo->gpu_mem,
49 0, offset, size);
50 nouveau_notifier_wait_nop(ctx, nmesa->syncNotifier,
51 NvSubMemFormat);
52 _mesa_memcpy(nbo->cpu_mem_sys + offset,
53 in_mem->map, size);
54 NOUVEAU_MEM_FREE(in_mem);
55 } else {
56 DEBUG("....direct VRAM copy\n");
57 /* worst case, copy directly from vram */
58 _mesa_memcpy(nbo->cpu_mem_sys + offset,
59 nbo->gpu_mem + offset,
60 size);
61 }
62 }
63
64 return GL_TRUE;
65 }
66
67 static GLboolean
68 nouveau_bo_upload_to_screen(GLcontext *ctx, GLuint offset, GLuint size,
69 struct gl_buffer_object *bo)
70 {
71 nouveauContextPtr nmesa = NOUVEAU_CONTEXT(ctx);
72 nouveau_buffer_object *nbo = (nouveau_buffer_object *)bo;
73 nouveau_mem *out_mem;
74
75 DEBUG("bo=%p, offset=%d, size=%d\n", bo, offset, size);
76
77 if (nbo->cpu_mem) {
78 if (nbo->cpu_mem != nbo->gpu_mem) {
79 DEBUG("..cpu_mem\n");
80 nouveau_memformat_flat_emit(ctx, nbo->gpu_mem,
81 nbo->cpu_mem,
82 offset, offset, size);
83 }
84 } else {
85 out_mem = nouveau_mem_alloc(ctx, NOUVEAU_MEM_AGP |
86 NOUVEAU_MEM_MAPPED,
87 size, 0);
88 if (out_mem) {
89 DEBUG("....via GART\n");
90 _mesa_memcpy(out_mem->map,
91 nbo->cpu_mem_sys + offset, size);
92 nouveau_memformat_flat_emit(ctx, nbo->gpu_mem, out_mem,
93 offset, 0, size);
94 nouveau_notifier_wait_nop(ctx, nmesa->syncNotifier,
95 NvSubMemFormat);
96 NOUVEAU_MEM_FREE(out_mem);
97 } else {
98 DEBUG("....direct VRAM copy\n");
99 _mesa_memcpy(nbo->gpu_mem->map + offset,
100 nbo->cpu_mem_sys + offset,
101 size);
102 }
103 }
104
105 return GL_TRUE;
106 }
107
108 GLboolean
109 nouveau_bo_move_in(GLcontext *ctx, struct gl_buffer_object *bo)
110 {
111 nouveau_buffer_object *nbo = (nouveau_buffer_object *)bo;
112
113 DEBUG("bo=%p\n", bo);
114
115 if (bo->OnCard)
116 return GL_TRUE;
117 assert(nbo->gpu_mem_flags);
118
119 nbo->gpu_mem = nouveau_mem_alloc(ctx, nbo->gpu_mem_flags |
120 NOUVEAU_MEM_MAPPED,
121 bo->Size, 0);
122 assert(nbo->gpu_mem);
123
124 if (nbo->cpu_mem_flags) {
125 if ((nbo->cpu_mem_flags|NOUVEAU_MEM_MAPPED) != nbo->gpu_mem->type) {
126 DEBUG("..need cpu_mem buffer\n");
127
128 nbo->cpu_mem = nouveau_mem_alloc(ctx,
129 nbo->cpu_mem_flags |
130 NOUVEAU_MEM_MAPPED,
131 bo->Size, 0);
132
133 if (nbo->cpu_mem) {
134 DEBUG("....alloc ok, kill sys_mem buffer\n");
135 _mesa_memcpy(nbo->cpu_mem->map,
136 nbo->cpu_mem_sys, bo->Size);
137 FREE(nbo->cpu_mem_sys);
138 }
139 } else {
140 DEBUG("..cpu direct access to GPU buffer\n");
141 nbo->cpu_mem = nbo->gpu_mem;
142 }
143 }
144 nouveau_bo_upload_to_screen(ctx, 0, bo->Size, bo);
145
146 bo->OnCard = GL_TRUE;
147 return GL_TRUE;
148 }
149
150 GLboolean
151 nouveau_bo_move_out(GLcontext *ctx, struct gl_buffer_object *bo)
152 {
153 nouveauContextPtr nmesa = NOUVEAU_CONTEXT(ctx);
154 nouveau_buffer_object *nbo = (nouveau_buffer_object *)bo;
155 GLuint nr_dirty;
156
157 DEBUG("bo=%p\n", bo);
158 if (!bo->OnCard)
159 return GL_TRUE;
160
161 nr_dirty = nouveau_bo_download_dirty(ctx, bo);
162 if (nbo->cpu_mem) {
163 if (nr_dirty && nbo->cpu_mem != nbo->gpu_mem)
164 nouveau_notifier_wait_nop(ctx, nmesa->syncNotifier,
165 NvSubMemFormat);
166 DEBUG("..destroy cpu_mem buffer\n");
167 nbo->cpu_mem_sys = malloc(bo->Size);
168 assert(nbo->cpu_mem_sys);
169 _mesa_memcpy(nbo->cpu_mem_sys, nbo->cpu_mem->map, bo->Size);
170 if (nbo->cpu_mem == nbo->gpu_mem)
171 nbo->cpu_mem = NULL;
172 else
173 NOUVEAU_MEM_FREE(nbo->cpu_mem);
174 }
175 NOUVEAU_MEM_FREE(nbo->gpu_mem);
176
177 bo->OnCard = GL_FALSE;
178 return GL_TRUE;
179 }
180
181 static void
182 nouveau_bo_choose_storage_method(GLcontext *ctx, GLenum usage,
183 struct gl_buffer_object *bo)
184 {
185 nouveau_buffer_object *nbo = (nouveau_buffer_object *)bo;
186 GLuint gpu_type = 0;
187 GLuint cpu_type = 0;
188
189 switch (usage) {
190 /* Client source, changes often, used by GL many times */
191 case GL_DYNAMIC_DRAW_ARB:
192 gpu_type = NOUVEAU_MEM_AGP | NOUVEAU_MEM_FB_ACCEPTABLE;
193 cpu_type = NOUVEAU_MEM_AGP;
194 break;
195 /* GL source, changes often, client reads many times */
196 case GL_DYNAMIC_READ_ARB:
197 /* Client source, specified once, used by GL many times */
198 case GL_STATIC_DRAW_ARB:
199 /* GL source, specified once, client reads many times */
200 case GL_STATIC_READ_ARB:
201 /* Client source, specified once, used by GL a few times */
202 case GL_STREAM_DRAW_ARB:
203 /* GL source, specified once, client reads a few times */
204 case GL_STREAM_READ_ARB:
205 /* GL source, changes often, used by GL many times*/
206 case GL_DYNAMIC_COPY_ARB:
207 /* GL source, specified once, used by GL many times */
208 case GL_STATIC_COPY_ARB:
209 /* GL source, specified once, used by GL a few times */
210 case GL_STREAM_COPY_ARB:
211 gpu_type = NOUVEAU_MEM_FB;
212 break;
213 default:
214 assert(0);
215 }
216
217 nbo->gpu_mem_flags = gpu_type;
218 nbo->cpu_mem_flags = cpu_type;
219 nbo->usage = usage;
220 }
221
222 void
223 nouveau_bo_init_storage(GLcontext *ctx, GLuint valid_gpu_access,
224 GLsizeiptrARB size,
225 const GLvoid *data,
226 GLenum usage,
227 struct gl_buffer_object *bo)
228 {
229 nouveau_buffer_object *nbo = (nouveau_buffer_object *)bo;
230
231 DEBUG("bo=%p\n", bo);
232
233 /* Free up previous buffers if we can't reuse them */
234 if (nbo->usage != usage ||
235 (nbo->gpu_mem && (nbo->gpu_mem->size != size))) {
236 if (nbo->cpu_mem_sys)
237 FREE(nbo->cpu_mem_sys);
238 if (nbo->cpu_mem) {
239 if (nbo->cpu_mem != nbo->gpu_mem)
240 NOUVEAU_MEM_FREE(nbo->cpu_mem);
241 else
242 nbo->cpu_mem = NULL;
243 }
244 if (nbo->gpu_mem)
245 NOUVEAU_MEM_FREE(nbo->gpu_mem);
246
247 bo->OnCard = GL_FALSE;
248 nbo->cpu_mem_sys = calloc(1, size);
249 }
250
251 nouveau_bo_choose_storage_method(ctx, usage, bo);
252 /* Force off flags that may not be ok for a given buffer */
253 nbo->gpu_mem_flags &= valid_gpu_access;
254
255 bo->Usage = usage;
256 bo->Size = size;
257
258 if (data) {
259 GLvoid *map = nouveau_bo_map(ctx, GL_WRITE_ONLY_ARB, bo);
260 _mesa_memcpy(map, data, size);
261 nouveau_bo_dirty_all(ctx, GL_FALSE, bo);
262 nouveau_bo_unmap(ctx, bo);
263 }
264 }
265
266 void *
267 nouveau_bo_map(GLcontext *ctx, GLenum access, struct gl_buffer_object *bo)
268 {
269 nouveauContextPtr nmesa = NOUVEAU_CONTEXT(ctx);
270 nouveau_buffer_object *nbo = (nouveau_buffer_object *)bo;
271
272 DEBUG("bo=%p, access=%s\n", bo, _mesa_lookup_enum_by_nr(access));
273
274 if (bo->OnCard &&
275 (access == GL_READ_ONLY_ARB || access == GL_READ_WRITE_ARB)) {
276 GLuint nr_dirty;
277
278 DEBUG("..on card\n");
279 nr_dirty = nouveau_bo_download_dirty(ctx, bo);
280
281 /* nouveau_bo_download_dirty won't wait unless it needs to
282 * free a temp buffer, which isn't the case if cpu_mem is
283 * present.
284 */
285 if (nr_dirty && nbo->cpu_mem && nbo->cpu_mem != nbo->gpu_mem)
286 nouveau_notifier_wait_nop(ctx, nmesa->syncNotifier,
287 NvSubMemFormat);
288 }
289
290 if (nbo->cpu_mem) {
291 DEBUG("..access via cpu_mem\n");
292 return nbo->cpu_mem->map;
293 } else {
294 DEBUG("..access via cpu_mem_sys\n");
295 return nbo->cpu_mem_sys;
296 }
297 }
298
299 void
300 nouveau_bo_unmap(GLcontext *ctx, struct gl_buffer_object *bo)
301 {
302 DEBUG("unmap bo=%p\n", bo);
303 }
304
305 uint32_t
306 nouveau_bo_gpu_ref(GLcontext *ctx, struct gl_buffer_object *bo)
307 {
308 nouveau_buffer_object *nbo = (nouveau_buffer_object *)bo;
309
310 assert(nbo->mapped == GL_FALSE);
311
312 DEBUG("gpu_ref\n");
313
314 if (!bo->OnCard) {
315 nouveau_bo_move_in(ctx, bo);
316 bo->OnCard = GL_TRUE;
317 }
318 nouveau_bo_upload_dirty(ctx, bo);
319
320 return nouveau_mem_gpu_offset_get(ctx, nbo->gpu_mem);
321 }
322
323 void
324 nouveau_bo_dirty_linear(GLcontext *ctx, GLboolean on_card,
325 uint32_t offset, uint32_t size,
326 struct gl_buffer_object *bo)
327 {
328 nouveau_buffer_object *nbo = (nouveau_buffer_object *)bo;
329 nouveau_bufferobj_dirty *dirty;
330 uint32_t start = offset;
331 uint32_t end = offset + size;
332 int i;
333
334 if (nbo->cpu_mem == nbo->gpu_mem)
335 return;
336
337 dirty = on_card ? &nbo->gpu_dirty : &nbo->cpu_dirty;
338
339 DEBUG("on_card=%d, offset=%d, size=%d, bo=%p\n",
340 on_card, offset, size, bo);
341
342 for (i=0; i<dirty->nr_dirty; i++) {
343 nouveau_bufferobj_region *r = &dirty->dirty[i];
344
345 /* already dirty */
346 if (start >= r->start && end <= r->end) {
347 DEBUG("..already dirty\n");
348 return;
349 }
350
351 /* add to the end of a region */
352 if (start >= r->start && start <= r->end) {
353 if (end > r->end) {
354 DEBUG("..extend end of region\n");
355 r->end = end;
356 return;
357 }
358 }
359
360 /* add to the start of a region */
361 if (start < r->start && end >= r->end) {
362 DEBUG("..extend start of region\n");
363 r->start = start;
364 /* .. and to the end */
365 if (end > r->end) {
366 DEBUG("....and end\n");
367 r->end = end;
368 }
369 return;
370 }
371 }
372
373 /* new region */
374 DEBUG("..new dirty\n");
375 dirty->nr_dirty++;
376 dirty->dirty = realloc(dirty->dirty,
377 sizeof(nouveau_bufferobj_region) *
378 dirty->nr_dirty);
379 dirty->dirty[dirty->nr_dirty - 1].start = start;
380 dirty->dirty[dirty->nr_dirty - 1].end = end;
381 }
382
383 void
384 nouveau_bo_dirty_all(GLcontext *ctx, GLboolean on_card,
385 struct gl_buffer_object *bo)
386 {
387 nouveau_buffer_object *nbo = (nouveau_buffer_object *)bo;
388 nouveau_bufferobj_dirty *dirty;
389
390 dirty = on_card ? &nbo->gpu_dirty : &nbo->cpu_dirty;
391
392 DEBUG("dirty all\n");
393 if (dirty->nr_dirty) {
394 FREE(dirty->dirty);
395 dirty->dirty = NULL;
396 dirty->nr_dirty = 0;
397 }
398
399 nouveau_bo_dirty_linear(ctx, on_card, 0, bo->Size, bo);
400 }
401
402 GLuint
403 nouveau_bo_upload_dirty(GLcontext *ctx, struct gl_buffer_object *bo)
404 {
405 nouveau_buffer_object *nbo = (nouveau_buffer_object *)bo;
406 nouveau_bufferobj_dirty *dirty = &nbo->cpu_dirty;
407 GLuint nr_dirty;
408 int i;
409
410 nr_dirty = dirty->nr_dirty;
411 if (!nr_dirty) {
412 DEBUG("clean\n");
413 return nr_dirty;
414 }
415
416 for (i=0; i<nr_dirty; i++) {
417 nouveau_bufferobj_region *r = &dirty->dirty[i];
418
419 DEBUG("dirty %d: o=0x%08x, s=0x%08x\n",
420 i, r->start, r->end - r->start);
421 nouveau_bo_upload_to_screen(ctx,
422 r->start, r->end - r->start, bo);
423 }
424
425 FREE(dirty->dirty);
426 dirty->dirty = NULL;
427 dirty->nr_dirty = 0;
428
429 return nr_dirty;
430 }
431
432 GLuint
433 nouveau_bo_download_dirty(GLcontext *ctx, struct gl_buffer_object *bo)
434 {
435 nouveau_buffer_object *nbo = (nouveau_buffer_object *)bo;
436 nouveau_bufferobj_dirty *dirty = &nbo->gpu_dirty;
437 GLuint nr_dirty;
438 int i;
439
440 nr_dirty = dirty->nr_dirty;
441 if (nr_dirty) {
442 DEBUG("clean\n");
443 return nr_dirty;
444 }
445
446 for (i=0; i<nr_dirty; i++) {
447 nouveau_bufferobj_region *r = &dirty->dirty[i];
448
449 DEBUG("dirty %d: o=0x%08x, s=0x%08x\n",
450 i, r->start, r->end - r->start);
451 nouveau_bo_download_from_screen(ctx,
452 r->start,
453 r->end - r->start, bo);
454 }
455
456 FREE(dirty->dirty);
457 dirty->dirty = NULL;
458 dirty->nr_dirty = 0;
459
460 return nr_dirty;
461 }
462
463 static void
464 nouveauBindBuffer(GLcontext *ctx, GLenum target, struct gl_buffer_object *obj)
465 {
466 }
467
468 static struct gl_buffer_object *
469 nouveauNewBufferObject(GLcontext *ctx, GLuint buffer, GLenum target)
470 {
471 nouveau_buffer_object *nbo;
472
473 nbo = CALLOC_STRUCT(nouveau_buffer_object_t);
474 if (nbo)
475 _mesa_initialize_buffer_object(&nbo->mesa, buffer, target);
476 DEBUG("bo=%p\n", nbo);
477
478 return nbo ? &nbo->mesa : NULL;
479 }
480
481 static void
482 nouveauDeleteBuffer(GLcontext *ctx, struct gl_buffer_object *obj)
483 {
484 nouveau_buffer_object *nbo = (nouveau_buffer_object *)obj;
485
486 if (nbo->gpu_dirty.nr_dirty)
487 FREE(nbo->gpu_dirty.dirty);
488 if (nbo->cpu_dirty.nr_dirty)
489 FREE(nbo->cpu_dirty.dirty);
490 if (nbo->cpu_mem) nouveau_mem_free(ctx, nbo->cpu_mem);
491 if (nbo->gpu_mem) nouveau_mem_free(ctx, nbo->gpu_mem);
492
493 _mesa_delete_buffer_object(ctx, obj);
494 }
495
496 static void
497 nouveauBufferData(GLcontext *ctx, GLenum target, GLsizeiptrARB size,
498 const GLvoid *data, GLenum usage,
499 struct gl_buffer_object *obj)
500 {
501 GLuint gpu_flags;
502
503 DEBUG("target=%s, size=%d, data=%p, usage=%s, obj=%p\n",
504 _mesa_lookup_enum_by_nr(target),
505 (GLuint)size, data,
506 _mesa_lookup_enum_by_nr(usage),
507 obj);
508
509 switch (target) {
510 case GL_ELEMENT_ARRAY_BUFFER_ARB:
511 gpu_flags = 0;
512 break;
513 default:
514 gpu_flags = NOUVEAU_BO_VRAM_OK | NOUVEAU_BO_GART_OK;
515 break;
516 }
517 nouveau_bo_init_storage(ctx, gpu_flags, size, data, usage, obj);
518 }
519
520 static void
521 nouveauBufferSubData(GLcontext *ctx, GLenum target, GLintptrARB offset,
522 GLsizeiptrARB size, const GLvoid *data,
523 struct gl_buffer_object *obj)
524 {
525 GLvoid *out;
526
527 DEBUG("target=%s, offset=0x%x, size=%d, data=%p, obj=%p\n",
528 _mesa_lookup_enum_by_nr(target),
529 (GLuint)offset, (GLuint)size, data, obj);
530
531 out = nouveau_bo_map(ctx, GL_WRITE_ONLY_ARB, obj);
532 _mesa_memcpy(out + offset, data, size);
533 nouveau_bo_dirty_linear(ctx, GL_FALSE, offset, size, obj);
534 nouveau_bo_unmap(ctx, obj);
535 }
536
537 static void
538 nouveauGetBufferSubData(GLcontext *ctx, GLenum target, GLintptrARB offset,
539 GLsizeiptrARB size, GLvoid *data,
540 struct gl_buffer_object *obj)
541 {
542 const GLvoid *in;
543
544 DEBUG("target=%s, offset=0x%x, size=%d, data=%p, obj=%p\n",
545 _mesa_lookup_enum_by_nr(target),
546 (GLuint)offset, (GLuint)size, data, obj);
547
548 in = nouveau_bo_map(ctx, GL_READ_ONLY_ARB, obj);
549 _mesa_memcpy(data, in + offset, size);
550 nouveau_bo_unmap(ctx, obj);
551 }
552
553 static void *
554 nouveauMapBuffer(GLcontext *ctx, GLenum target, GLenum access,
555 struct gl_buffer_object *obj)
556 {
557 DEBUG("target=%s, access=%s, obj=%p\n",
558 _mesa_lookup_enum_by_nr(target),
559 _mesa_lookup_enum_by_nr(access),
560 obj
561 );
562
563 /* Already mapped.. */
564 if (obj->Pointer)
565 return NULL;
566
567 /* Have to pass READ_WRITE here, nouveau_bo_map will only ensure that
568 * the cpu_mem buffer is up-to-date if we ask for read access.
569 *
570 * However, even if the client only asks for write access, we're still
571 * forced to reupload the entire buffer. So, we need the cpu_mem buffer
572 * to have the correct data all the time.
573 */
574 obj->Pointer = nouveau_bo_map(ctx, GL_READ_WRITE_ARB, obj);
575
576 /* The GL spec says that a client attempting to write to a bufferobj
577 * mapped READ_ONLY object may have unpredictable results, possibly
578 * even program termination.
579 *
580 * We're going to use this, and only mark the buffer as dirtied if
581 * the client asks for write access.
582 */
583 if (target != GL_READ_ONLY_ARB) {
584 /* We have no way of knowing what was modified by the client,
585 * so the entire buffer gets dirtied. */
586 nouveau_bo_dirty_all(ctx, GL_FALSE, obj);
587 }
588
589 return obj->Pointer;
590 }
591
592 static GLboolean
593 nouveauUnmapBuffer(GLcontext *ctx, GLenum target, struct gl_buffer_object *obj)
594 {
595 DEBUG("target=%s, obj=%p\n", _mesa_lookup_enum_by_nr(target), obj);
596
597 assert(obj->Pointer);
598
599 nouveau_bo_unmap(ctx, obj);
600 obj->Pointer = NULL;
601 return GL_TRUE;
602 }
603
604 void
605 nouveauInitBufferObjects(GLcontext *ctx)
606 {
607 ctx->Driver.BindBuffer = nouveauBindBuffer;
608 ctx->Driver.NewBufferObject = nouveauNewBufferObject;
609 ctx->Driver.DeleteBuffer = nouveauDeleteBuffer;
610 ctx->Driver.BufferData = nouveauBufferData;
611 ctx->Driver.BufferSubData = nouveauBufferSubData;
612 ctx->Driver.GetBufferSubData = nouveauGetBufferSubData;
613 ctx->Driver.MapBuffer = nouveauMapBuffer;
614 ctx->Driver.UnmapBuffer = nouveauUnmapBuffer;
615 }
616