30a235a2e53621b9a45f915981754c89e81860b1
[mesa.git] / src / mesa / drivers / dri / i965 / bufmgr_fake.c
1 /**************************************************************************
2 *
3 * Copyright 2006 Tungsten Graphics, Inc., Cedar Park, Texas.
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 TUNGSTEN GRAPHICS 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 /* Originally a fake version of the buffer manager so that we can
29 * prototype the changes in a driver fairly quickly, has been fleshed
30 * out to a fully functional interim solution.
31 *
32 * Basically wraps the old style memory management in the new
33 * programming interface, but is more expressive and avoids many of
34 * the bugs in the old texture manager.
35 */
36 #include "bufmgr.h"
37
38 #include "intel_context.h"
39 #include "intel_ioctl.h"
40 #include "intel_batchbuffer.h"
41
42 #include "simple_list.h"
43 #include "mm.h"
44 #include "imports.h"
45
46 #define BM_POOL_MAX 8
47
48 /* Internal flags:
49 */
50 #define BM_NO_BACKING_STORE 0x2000
51 #define BM_NO_FENCE_SUBDATA 0x4000
52
53
54 static int check_fenced( struct intel_context *intel );
55
56 static int nr_attach = 0;
57
58 /* Wrapper around mm.c's mem_block, which understands that you must
59 * wait for fences to expire before memory can be freed. This is
60 * specific to our use of memcpy for uploads - an upload that was
61 * processed through the command queue wouldn't need to care about
62 * fences.
63 */
64 struct block {
65 struct block *next, *prev;
66 struct pool *pool; /* BM_MEM_AGP */
67 struct mem_block *mem; /* BM_MEM_AGP */
68
69 unsigned referenced:1;
70 unsigned on_hardware:1;
71 unsigned fenced:1;
72
73
74 unsigned fence; /* BM_MEM_AGP, Split to read_fence, write_fence */
75
76 struct buffer *buf;
77 void *virtual;
78 };
79
80
81 struct buffer {
82 unsigned id; /* debug only */
83 const char *name;
84 unsigned size;
85
86 unsigned mapped:1;
87 unsigned dirty:1;
88 unsigned aub_dirty:1;
89 unsigned alignment:13;
90 unsigned flags:16;
91
92 struct block *block;
93 void *backing_store;
94 void (*invalidate_cb)( struct intel_context *, void * );
95 void *invalidate_ptr;
96 };
97
98 struct pool {
99 unsigned size;
100 unsigned low_offset;
101 struct buffer *static_buffer;
102 unsigned flags;
103 struct mem_block *heap;
104 void *virtual;
105 struct block lru; /* only allocated, non-fence-pending blocks here */
106 };
107
108 struct bufmgr {
109 _glthread_Mutex mutex; /**< for thread safety */
110 struct pool pool[BM_POOL_MAX];
111 unsigned nr_pools;
112
113 unsigned buf_nr; /* for generating ids */
114
115 struct block referenced; /* after bmBufferOffset */
116 struct block on_hardware; /* after bmValidateBuffers */
117 struct block fenced; /* after bmFenceBuffers (mi_flush, emit irq, write dword) */
118 /* then to pool->lru or free() */
119
120 unsigned ctxId;
121 unsigned last_fence;
122 unsigned free_on_hardware;
123
124 unsigned fail:1;
125 unsigned need_fence:1;
126 };
127
128 #define MAXFENCE 0x7fffffff
129
130 static GLboolean FENCE_LTE( unsigned a, unsigned b )
131 {
132 if (a == b)
133 return GL_TRUE;
134
135 if (a < b && b - a < (1<<24))
136 return GL_TRUE;
137
138 if (a > b && MAXFENCE - a + b < (1<<24))
139 return GL_TRUE;
140
141 return GL_FALSE;
142 }
143
144 int bmTestFence( struct intel_context *intel, unsigned fence )
145 {
146 /* Slight problem with wrap-around:
147 */
148 return fence == 0 || FENCE_LTE(fence, intel->sarea->last_dispatch);
149 }
150
151 #define LOCK(bm) \
152 int dolock = nr_attach > 1; \
153 if (dolock) _glthread_LOCK_MUTEX(bm->mutex)
154
155 #define UNLOCK(bm) \
156 if (dolock) _glthread_UNLOCK_MUTEX(bm->mutex)
157
158
159
160 static GLboolean alloc_from_pool( struct intel_context *intel,
161 unsigned pool_nr,
162 struct buffer *buf )
163 {
164 struct bufmgr *bm = intel->bm;
165 struct pool *pool = &bm->pool[pool_nr];
166 struct block *block = (struct block *)calloc(sizeof *block, 1);
167 GLuint sz, align = (1<<buf->alignment);
168
169 if (!block)
170 return GL_FALSE;
171
172 sz = (buf->size + align-1) & ~(align-1);
173
174 block->mem = mmAllocMem(pool->heap,
175 sz,
176 buf->alignment, 0);
177 if (!block->mem) {
178 free(block);
179 return GL_FALSE;
180 }
181
182 make_empty_list(block);
183
184 /* Insert at head or at tail???
185 */
186 insert_at_tail(&pool->lru, block);
187
188 block->pool = pool;
189 block->virtual = pool->virtual + block->mem->ofs;
190 block->buf = buf;
191
192 buf->block = block;
193
194 return GL_TRUE;
195 }
196
197
198
199
200
201
202
203
204 /* Release the card storage associated with buf:
205 */
206 static void free_block( struct intel_context *intel, struct block *block )
207 {
208 DBG("free block %p\n", block);
209
210 if (!block)
211 return;
212
213 check_fenced(intel);
214
215 if (block->referenced) {
216 _mesa_printf("tried to free block on referenced list\n");
217 assert(0);
218 }
219 else if (block->on_hardware) {
220 block->buf = NULL;
221 intel->bm->free_on_hardware += block->mem->size;
222 }
223 else if (block->fenced) {
224 block->buf = NULL;
225 }
226 else {
227 DBG(" - free immediately\n");
228 remove_from_list(block);
229
230 mmFreeMem(block->mem);
231 free(block);
232 }
233 }
234
235
236 static void alloc_backing_store( struct intel_context *intel, struct buffer *buf )
237 {
238 assert(!buf->backing_store);
239 assert(!(buf->flags & (BM_NO_EVICT|BM_NO_BACKING_STORE)));
240
241 buf->backing_store = ALIGN_MALLOC(buf->size, 64);
242 }
243
244 static void free_backing_store( struct intel_context *intel, struct buffer *buf )
245 {
246 assert(!(buf->flags & (BM_NO_EVICT|BM_NO_BACKING_STORE)));
247
248 if (buf->backing_store) {
249 ALIGN_FREE(buf->backing_store);
250 buf->backing_store = NULL;
251 }
252 }
253
254
255
256
257
258
259 static void set_dirty( struct intel_context *intel,
260 struct buffer *buf )
261 {
262 if (buf->flags & BM_NO_BACKING_STORE)
263 buf->invalidate_cb(intel, buf->invalidate_ptr);
264
265 assert(!(buf->flags & BM_NO_EVICT));
266
267 DBG("set_dirty - buf %d\n", buf->id);
268 buf->dirty = 1;
269 }
270
271
272 static int evict_lru( struct intel_context *intel, GLuint max_fence, GLuint *pool )
273 {
274 struct bufmgr *bm = intel->bm;
275 struct block *block, *tmp;
276 int i;
277
278 DBG("%s\n", __FUNCTION__);
279
280 for (i = 0; i < bm->nr_pools; i++) {
281 if (!(bm->pool[i].flags & BM_NO_EVICT)) {
282 foreach_s(block, tmp, &bm->pool[i].lru) {
283
284 if (block->buf &&
285 (block->buf->flags & BM_NO_FENCE_SUBDATA))
286 continue;
287
288 if (block->fence && max_fence &&
289 !FENCE_LTE(block->fence, max_fence))
290 return 0;
291
292 set_dirty(intel, block->buf);
293 block->buf->block = NULL;
294
295 free_block(intel, block);
296 *pool = i;
297 return 1;
298 }
299 }
300 }
301
302
303 return 0;
304 }
305
306
307 #define foreach_s_rev(ptr, t, list) \
308 for(ptr=(list)->prev,t=(ptr)->prev; list != ptr; ptr=t, t=(t)->prev)
309
310 static int evict_mru( struct intel_context *intel, GLuint *pool )
311 {
312 struct bufmgr *bm = intel->bm;
313 struct block *block, *tmp;
314 int i;
315
316 DBG("%s\n", __FUNCTION__);
317
318 for (i = 0; i < bm->nr_pools; i++) {
319 if (!(bm->pool[i].flags & BM_NO_EVICT)) {
320 foreach_s_rev(block, tmp, &bm->pool[i].lru) {
321
322 if (block->buf &&
323 (block->buf->flags & BM_NO_FENCE_SUBDATA))
324 continue;
325
326 set_dirty(intel, block->buf);
327 block->buf->block = NULL;
328
329 free_block(intel, block);
330 *pool = i;
331 return 1;
332 }
333 }
334 }
335
336
337 return 0;
338 }
339
340
341
342 static int check_fenced( struct intel_context *intel )
343 {
344 struct bufmgr *bm = intel->bm;
345 struct block *block, *tmp;
346 int ret = 0;
347
348 foreach_s(block, tmp, &bm->fenced ) {
349 assert(block->fenced);
350
351 if (bmTestFence(intel, block->fence)) {
352
353 block->fenced = 0;
354
355 if (!block->buf) {
356 DBG("delayed free: offset %x sz %x\n", block->mem->ofs, block->mem->size);
357 remove_from_list(block);
358 mmFreeMem(block->mem);
359 free(block);
360 }
361 else {
362 DBG("return to lru: offset %x sz %x\n", block->mem->ofs, block->mem->size);
363 move_to_tail(&block->pool->lru, block);
364 }
365
366 ret = 1;
367 }
368 else {
369 /* Blocks are ordered by fence, so if one fails, all from
370 * here will fail also:
371 */
372 break;
373 }
374 }
375
376 /* Also check the referenced list:
377 */
378 foreach_s(block, tmp, &bm->referenced ) {
379 if (block->fenced &&
380 bmTestFence(intel, block->fence)) {
381 block->fenced = 0;
382 }
383 }
384
385
386 DBG("%s: %d\n", __FUNCTION__, ret);
387 return ret;
388 }
389
390
391
392 static void fence_blocks( struct intel_context *intel,
393 unsigned fence )
394 {
395 struct bufmgr *bm = intel->bm;
396 struct block *block, *tmp;
397
398 foreach_s (block, tmp, &bm->on_hardware) {
399 DBG("Fence block %p (sz 0x%x buf %p) with fence %d\n", block,
400 block->mem->size, block->buf, fence);
401 block->fence = fence;
402
403 block->on_hardware = 0;
404 block->fenced = 1;
405
406 /* Move to tail of pending list here
407 */
408 move_to_tail(&bm->fenced, block);
409 }
410
411 /* Also check the referenced list:
412 */
413 foreach_s (block, tmp, &bm->referenced) {
414 if (block->on_hardware) {
415 DBG("Fence block %p (sz 0x%x buf %p) with fence %d\n", block,
416 block->mem->size, block->buf, fence);
417
418 block->fence = fence;
419 block->on_hardware = 0;
420 block->fenced = 1;
421 }
422 }
423
424
425 bm->last_fence = fence;
426 assert(is_empty_list(&bm->on_hardware));
427 }
428
429
430
431
432 static GLboolean alloc_block( struct intel_context *intel,
433 struct buffer *buf )
434 {
435 struct bufmgr *bm = intel->bm;
436 int i;
437
438 assert(intel->locked);
439
440 DBG("%s 0x%x bytes (%s)\n", __FUNCTION__, buf->size, buf->name);
441
442 for (i = 0; i < bm->nr_pools; i++) {
443 if (!(bm->pool[i].flags & BM_NO_ALLOC) &&
444 alloc_from_pool(intel, i, buf)) {
445
446 DBG("%s --> 0x%x (sz %x)\n", __FUNCTION__,
447 buf->block->mem->ofs, buf->block->mem->size);
448
449 return GL_TRUE;
450 }
451 }
452
453 DBG("%s --> fail\n", __FUNCTION__);
454 return GL_FALSE;
455 }
456
457
458 static GLboolean evict_and_alloc_block( struct intel_context *intel,
459 struct buffer *buf )
460 {
461 GLuint pool;
462 struct bufmgr *bm = intel->bm;
463
464 assert(buf->block == NULL);
465
466 /* Put a cap on the amount of free memory we'll allow to accumulate
467 * before emitting a fence.
468 */
469 if (bm->free_on_hardware > 1 * 1024 * 1024) {
470 DBG("fence for free space: %x\n", bm->free_on_hardware);
471 bmSetFence(intel);
472 }
473
474 /* Search for already free memory:
475 */
476 if (alloc_block(intel, buf))
477 return GL_TRUE;
478
479 /* Look for memory that may have become free:
480 */
481 if (check_fenced(intel) &&
482 alloc_block(intel, buf))
483 return GL_TRUE;
484
485 /* Look for memory blocks not used for >1 frame:
486 */
487 while (evict_lru(intel, intel->second_last_swap_fence, &pool))
488 if (alloc_from_pool(intel, pool, buf))
489 return GL_TRUE;
490
491 /* If we're not thrashing, allow lru eviction to dig deeper into
492 * recently used textures. We'll probably be thrashing soon:
493 */
494 if (!intel->thrashing) {
495 while (evict_lru(intel, 0, &pool))
496 if (alloc_from_pool(intel, pool, buf))
497 return GL_TRUE;
498 }
499
500 /* Keep thrashing counter alive?
501 */
502 if (intel->thrashing)
503 intel->thrashing = 20;
504
505 /* Wait on any already pending fences - here we are waiting for any
506 * freed memory that has been submitted to hardware and fenced to
507 * become available:
508 */
509 while (!is_empty_list(&bm->fenced)) {
510 GLuint fence = bm->fenced.next->fence;
511 bmFinishFence(intel, fence);
512
513 if (alloc_block(intel, buf))
514 return GL_TRUE;
515 }
516
517
518 /*
519 */
520 if (!is_empty_list(&bm->on_hardware)) {
521 bmSetFence(intel);
522
523 while (!is_empty_list(&bm->fenced)) {
524 GLuint fence = bm->fenced.next->fence;
525 bmFinishFence(intel, fence);
526 }
527
528 if (!intel->thrashing) {
529 DBG("thrashing\n");
530 }
531 intel->thrashing = 20;
532
533 if (alloc_block(intel, buf))
534 return GL_TRUE;
535 }
536
537 while (evict_mru(intel, &pool))
538 if (alloc_from_pool(intel, pool, buf))
539 return GL_TRUE;
540
541 DBG("%s 0x%x bytes failed\n", __FUNCTION__, buf->size);
542
543 assert(is_empty_list(&bm->on_hardware));
544 assert(is_empty_list(&bm->fenced));
545
546 return GL_FALSE;
547 }
548
549
550
551
552
553
554
555
556
557
558 /***********************************************************************
559 * Public functions
560 */
561
562
563 /* The initialization functions are skewed in the fake implementation.
564 * This call would be to attach to an existing manager, rather than to
565 * create a local one.
566 */
567 struct bufmgr *bm_fake_intel_Attach( struct intel_context *intel )
568 {
569 _glthread_DECLARE_STATIC_MUTEX(initMutex);
570 static struct bufmgr bm;
571
572 /* This function needs a mutex of its own...
573 */
574 _glthread_LOCK_MUTEX(initMutex);
575
576 if (nr_attach == 0) {
577 _glthread_INIT_MUTEX(bm.mutex);
578
579 make_empty_list(&bm.referenced);
580 make_empty_list(&bm.fenced);
581 make_empty_list(&bm.on_hardware);
582
583 /* The context id of any of the share group. This won't be used
584 * in communication with the kernel, so it doesn't matter if
585 * this context is eventually deleted.
586 */
587 bm.ctxId = intel->hHWContext;
588 }
589
590 nr_attach++;
591
592 _glthread_UNLOCK_MUTEX(initMutex);
593
594 return &bm;
595 }
596
597
598
599 /* The virtual pointer would go away in a true implementation.
600 */
601 int bmInitPool( struct intel_context *intel,
602 unsigned long low_offset,
603 void *low_virtual,
604 unsigned long size,
605 unsigned flags)
606 {
607 struct bufmgr *bm = intel->bm;
608 int retval = 0;
609
610 LOCK(bm);
611 {
612 GLuint i;
613
614 for (i = 0; i < bm->nr_pools; i++) {
615 if (bm->pool[i].low_offset == low_offset &&
616 bm->pool[i].size == size) {
617 retval = i;
618 goto out;
619 }
620 }
621
622
623 if (bm->nr_pools >= BM_POOL_MAX)
624 retval = -1;
625 else {
626 i = bm->nr_pools++;
627
628 DBG("bmInitPool %d low_offset %x sz %x\n",
629 i, low_offset, size);
630
631 bm->pool[i].low_offset = low_offset;
632 bm->pool[i].size = size;
633 bm->pool[i].heap = mmInit( low_offset, size );
634 bm->pool[i].virtual = low_virtual - low_offset;
635 bm->pool[i].flags = flags;
636
637 make_empty_list(&bm->pool[i].lru);
638
639 retval = i;
640 }
641 }
642 out:
643 UNLOCK(bm);
644 return retval;
645 }
646
647 static struct buffer *do_GenBuffer(struct intel_context *intel, const char *name, int align)
648 {
649 struct bufmgr *bm = intel->bm;
650 struct buffer *buf = calloc(sizeof(*buf), 1);
651
652 buf->id = ++bm->buf_nr;
653 buf->name = name;
654 buf->alignment = align;
655 buf->flags = BM_MEM_AGP|BM_MEM_VRAM|BM_MEM_LOCAL;
656
657 return buf;
658 }
659
660
661 void *bmFindVirtual( struct intel_context *intel,
662 unsigned int offset,
663 size_t sz )
664 {
665 struct bufmgr *bm = intel->bm;
666 int i;
667
668 for (i = 0; i < bm->nr_pools; i++)
669 if (offset >= bm->pool[i].low_offset &&
670 offset + sz <= bm->pool[i].low_offset + bm->pool[i].size)
671 return bm->pool[i].virtual + offset;
672
673 return NULL;
674 }
675
676
677 void bmGenBuffers(struct intel_context *intel,
678 const char *name, unsigned n,
679 struct buffer **buffers,
680 int align )
681 {
682 struct bufmgr *bm = intel->bm;
683 LOCK(bm);
684 {
685 int i;
686
687 for (i = 0; i < n; i++)
688 buffers[i] = do_GenBuffer(intel, name, align);
689 }
690 UNLOCK(bm);
691 }
692
693
694 void bmDeleteBuffers(struct intel_context *intel, unsigned n, struct buffer **buffers)
695 {
696 struct bufmgr *bm = intel->bm;
697
698 LOCK(bm);
699 {
700 unsigned i;
701
702 for (i = 0; i < n; i++) {
703 struct buffer *buf = buffers[i];
704
705 if (buf && buf->block)
706 free_block(intel, buf->block);
707
708 if (buf)
709 free(buf);
710 }
711 }
712 UNLOCK(bm);
713 }
714
715
716
717
718 /* Hook to inform faked buffer manager about fixed-position
719 * front,depth,back buffers. These may move to a fully memory-managed
720 * scheme, or they may continue to be managed as is. It will probably
721 * be useful to pass a fixed offset here one day.
722 */
723 struct buffer *bmGenBufferStatic(struct intel_context *intel,
724 unsigned pool )
725 {
726 struct bufmgr *bm = intel->bm;
727 struct buffer *buf;
728 LOCK(bm);
729 {
730 assert(bm->pool[pool].flags & BM_NO_EVICT);
731 assert(bm->pool[pool].flags & BM_NO_MOVE);
732
733 if (bm->pool[pool].static_buffer)
734 buf = bm->pool[pool].static_buffer;
735 else {
736 buf = do_GenBuffer(intel, "static", 12);
737
738 bm->pool[pool].static_buffer = buf;
739 assert(!buf->block);
740
741 buf->size = bm->pool[pool].size;
742 buf->flags = bm->pool[pool].flags;
743 buf->alignment = 12;
744
745 if (!alloc_from_pool(intel, pool, buf))
746 assert(0);
747 }
748 }
749 UNLOCK(bm);
750 return buf;
751 }
752
753
754 static void wait_quiescent(struct intel_context *intel,
755 struct block *block)
756 {
757 if (block->on_hardware) {
758 assert(intel->bm->need_fence);
759 bmSetFence(intel);
760 assert(!block->on_hardware);
761 }
762
763
764 if (block->fenced) {
765 bmFinishFence(intel, block->fence);
766 }
767
768 assert(!block->on_hardware);
769 assert(!block->fenced);
770 }
771
772
773
774 /* If buffer size changes, free and reallocate. Otherwise update in
775 * place.
776 */
777 int bmBufferData(struct intel_context *intel,
778 struct buffer *buf,
779 unsigned size,
780 const void *data,
781 unsigned flags )
782 {
783 struct bufmgr *bm = intel->bm;
784 int retval = 0;
785
786 LOCK(bm);
787 {
788 DBG("bmBufferData %d sz 0x%x data: %p\n", buf->id, size, data);
789
790 assert(!buf->mapped);
791
792 if (buf->block) {
793 struct block *block = buf->block;
794
795 /* Optimistic check to see if we can reuse the block -- not
796 * required for correctness:
797 */
798 if (block->fenced)
799 check_fenced(intel);
800
801 if (block->on_hardware ||
802 block->fenced ||
803 (buf->size && buf->size != size) ||
804 (data == NULL)) {
805
806 assert(!block->referenced);
807
808 free_block(intel, block);
809 buf->block = NULL;
810 buf->dirty = 1;
811 }
812 }
813
814 buf->size = size;
815 if (buf->block) {
816 assert (buf->block->mem->size >= size);
817 }
818
819 if (buf->flags & (BM_NO_BACKING_STORE|BM_NO_EVICT)) {
820
821 assert(intel->locked || data == NULL);
822
823 if (data != NULL) {
824 if (!buf->block && !evict_and_alloc_block(intel, buf)) {
825 bm->fail = 1;
826 retval = -1;
827 goto out;
828 }
829
830 wait_quiescent(intel, buf->block);
831
832 DBG("bmBufferData %d offset 0x%x sz 0x%x\n",
833 buf->id, buf->block->mem->ofs, size);
834
835 assert(buf->block->virtual == buf->block->pool->virtual + buf->block->mem->ofs);
836
837 do_memcpy(buf->block->virtual, data, size);
838 }
839 buf->dirty = 0;
840 }
841 else {
842 DBG("%s - set buf %d dirty\n", __FUNCTION__, buf->id);
843 set_dirty(intel, buf);
844 free_backing_store(intel, buf);
845
846 if (data != NULL) {
847 alloc_backing_store(intel, buf);
848 do_memcpy(buf->backing_store, data, size);
849 }
850 }
851 }
852 out:
853 UNLOCK(bm);
854 return retval;
855 }
856
857
858 /* Update the buffer in place, in whatever space it is currently resident:
859 */
860 int bmBufferSubData(struct intel_context *intel,
861 struct buffer *buf,
862 unsigned offset,
863 unsigned size,
864 const void *data )
865 {
866 struct bufmgr *bm = intel->bm;
867 int retval = 0;
868
869 if (size == 0)
870 return 0;
871
872 LOCK(bm);
873 {
874 DBG("bmBufferSubdata %d offset 0x%x sz 0x%x\n", buf->id, offset, size);
875
876 assert(offset+size <= buf->size);
877
878 if (buf->flags & (BM_NO_EVICT|BM_NO_BACKING_STORE)) {
879
880 assert(intel->locked);
881
882 if (!buf->block && !evict_and_alloc_block(intel, buf)) {
883 bm->fail = 1;
884 retval = -1;
885 goto out;
886 }
887
888 if (!(buf->flags & BM_NO_FENCE_SUBDATA))
889 wait_quiescent(intel, buf->block);
890
891 buf->dirty = 0;
892
893 do_memcpy(buf->block->virtual + offset, data, size);
894 }
895 else {
896 DBG("%s - set buf %d dirty\n", __FUNCTION__, buf->id);
897 set_dirty(intel, buf);
898
899 if (buf->backing_store == NULL)
900 alloc_backing_store(intel, buf);
901
902 do_memcpy(buf->backing_store + offset, data, size);
903 }
904 }
905 out:
906 UNLOCK(bm);
907 return retval;
908 }
909
910
911
912 int bmBufferDataAUB(struct intel_context *intel,
913 struct buffer *buf,
914 unsigned size,
915 const void *data,
916 unsigned flags,
917 unsigned aubtype,
918 unsigned aubsubtype )
919 {
920 int retval = bmBufferData(intel, buf, size, data, flags);
921
922
923 /* This only works because in this version of the buffer manager we
924 * allocate all buffers statically in agp space and so can emit the
925 * uploads to the aub file with the correct offsets as they happen.
926 */
927 if (retval == 0 && data && intel->aub_file) {
928
929 if (buf->block && !buf->dirty) {
930 intel->vtbl.aub_gtt_data(intel,
931 buf->block->mem->ofs,
932 buf->block->virtual,
933 size,
934 aubtype,
935 aubsubtype);
936 buf->aub_dirty = 0;
937 }
938 }
939
940 return retval;
941 }
942
943
944 int bmBufferSubDataAUB(struct intel_context *intel,
945 struct buffer *buf,
946 unsigned offset,
947 unsigned size,
948 const void *data,
949 unsigned aubtype,
950 unsigned aubsubtype )
951 {
952 int retval = bmBufferSubData(intel, buf, offset, size, data);
953
954
955 /* This only works because in this version of the buffer manager we
956 * allocate all buffers statically in agp space and so can emit the
957 * uploads to the aub file with the correct offsets as they happen.
958 */
959 if (intel->aub_file) {
960 if (retval == 0 && buf->block && !buf->dirty)
961 intel->vtbl.aub_gtt_data(intel,
962 buf->block->mem->ofs + offset,
963 ((const char *)buf->block->virtual) + offset,
964 size,
965 aubtype,
966 aubsubtype);
967 }
968
969 return retval;
970 }
971
972 void bmUnmapBufferAUB( struct intel_context *intel,
973 struct buffer *buf,
974 unsigned aubtype,
975 unsigned aubsubtype )
976 {
977 bmUnmapBuffer(intel, buf);
978
979 if (intel->aub_file) {
980 /* Hack - exclude the framebuffer mappings. If you removed
981 * this, you'd get very big aubfiles, but you *would* be able to
982 * see fallback rendering.
983 */
984 if (buf->block && !buf->dirty && buf->block->pool == &intel->bm->pool[0]) {
985 buf->aub_dirty = 1;
986 }
987 }
988 }
989
990 unsigned bmBufferOffset(struct intel_context *intel,
991 struct buffer *buf)
992 {
993 struct bufmgr *bm = intel->bm;
994 unsigned retval = 0;
995
996 LOCK(bm);
997 {
998 assert(intel->locked);
999
1000 if (!buf->block &&
1001 !evict_and_alloc_block(intel, buf)) {
1002 bm->fail = 1;
1003 retval = ~0;
1004 }
1005 else {
1006 assert(buf->block);
1007 assert(buf->block->buf == buf);
1008
1009 DBG("Add buf %d (block %p, dirty %d) to referenced list\n", buf->id, buf->block,
1010 buf->dirty);
1011
1012 move_to_tail(&bm->referenced, buf->block);
1013 buf->block->referenced = 1;
1014
1015 retval = buf->block->mem->ofs;
1016 }
1017 }
1018 UNLOCK(bm);
1019
1020 return retval;
1021 }
1022
1023
1024
1025 /* Extract data from the buffer:
1026 */
1027 void bmBufferGetSubData(struct intel_context *intel,
1028 struct buffer *buf,
1029 unsigned offset,
1030 unsigned size,
1031 void *data )
1032 {
1033 struct bufmgr *bm = intel->bm;
1034
1035 LOCK(bm);
1036 {
1037 DBG("bmBufferSubdata %d offset 0x%x sz 0x%x\n", buf->id, offset, size);
1038
1039 if (buf->flags & (BM_NO_EVICT|BM_NO_BACKING_STORE)) {
1040 if (buf->block && size) {
1041 wait_quiescent(intel, buf->block);
1042 do_memcpy(data, buf->block->virtual + offset, size);
1043 }
1044 }
1045 else {
1046 if (buf->backing_store && size) {
1047 do_memcpy(data, buf->backing_store + offset, size);
1048 }
1049 }
1050 }
1051 UNLOCK(bm);
1052 }
1053
1054
1055 /* Return a pointer to whatever space the buffer is currently resident in:
1056 */
1057 void *bmMapBuffer( struct intel_context *intel,
1058 struct buffer *buf,
1059 unsigned flags )
1060 {
1061 struct bufmgr *bm = intel->bm;
1062 void *retval = NULL;
1063
1064 LOCK(bm);
1065 {
1066 DBG("bmMapBuffer %d\n", buf->id);
1067
1068 if (buf->mapped) {
1069 _mesa_printf("%s: already mapped\n", __FUNCTION__);
1070 retval = NULL;
1071 }
1072 else if (buf->flags & (BM_NO_BACKING_STORE|BM_NO_EVICT)) {
1073
1074 assert(intel->locked);
1075
1076 if (!buf->block && !evict_and_alloc_block(intel, buf)) {
1077 DBG("%s: alloc failed\n", __FUNCTION__);
1078 bm->fail = 1;
1079 retval = NULL;
1080 }
1081 else {
1082 assert(buf->block);
1083 buf->dirty = 0;
1084
1085 if (!(buf->flags & BM_NO_FENCE_SUBDATA))
1086 wait_quiescent(intel, buf->block);
1087
1088 buf->mapped = 1;
1089 retval = buf->block->virtual;
1090 }
1091 }
1092 else {
1093 DBG("%s - set buf %d dirty\n", __FUNCTION__, buf->id);
1094 set_dirty(intel, buf);
1095
1096 if (buf->backing_store == 0)
1097 alloc_backing_store(intel, buf);
1098
1099 buf->mapped = 1;
1100 retval = buf->backing_store;
1101 }
1102 }
1103 UNLOCK(bm);
1104 return retval;
1105 }
1106
1107 void bmUnmapBuffer( struct intel_context *intel, struct buffer *buf )
1108 {
1109 struct bufmgr *bm = intel->bm;
1110
1111 LOCK(bm);
1112 {
1113 DBG("bmUnmapBuffer %d\n", buf->id);
1114 buf->mapped = 0;
1115 }
1116 UNLOCK(bm);
1117 }
1118
1119
1120
1121
1122 /* This is the big hack that turns on BM_NO_BACKING_STORE. Basically
1123 * says that an external party will maintain the backing store, eg
1124 * Mesa's local copy of texture data.
1125 */
1126 void bmBufferSetInvalidateCB(struct intel_context *intel,
1127 struct buffer *buf,
1128 void (*invalidate_cb)( struct intel_context *, void *ptr ),
1129 void *ptr,
1130 GLboolean dont_fence_subdata)
1131 {
1132 struct bufmgr *bm = intel->bm;
1133
1134 LOCK(bm);
1135 {
1136 if (buf->backing_store)
1137 free_backing_store(intel, buf);
1138
1139 buf->flags |= BM_NO_BACKING_STORE;
1140
1141 if (dont_fence_subdata)
1142 buf->flags |= BM_NO_FENCE_SUBDATA;
1143
1144 DBG("bmBufferSetInvalidateCB set buf %d dirty\n", buf->id);
1145 buf->dirty = 1;
1146 buf->invalidate_cb = invalidate_cb;
1147 buf->invalidate_ptr = ptr;
1148
1149 /* Note that it is invalid right from the start. Also note
1150 * invalidate_cb is called with the bufmgr locked, so cannot
1151 * itself make bufmgr calls.
1152 */
1153 invalidate_cb( intel, ptr );
1154 }
1155 UNLOCK(bm);
1156 }
1157
1158
1159
1160
1161
1162
1163
1164 /* This is only protected against thread interactions by the DRI lock
1165 * and the policy of ensuring that all dma is flushed prior to
1166 * releasing that lock. Otherwise you might have two threads building
1167 * up a list of buffers to validate at once.
1168 */
1169 int bmValidateBuffers( struct intel_context *intel )
1170 {
1171 struct bufmgr *bm = intel->bm;
1172 int retval = 0;
1173
1174 LOCK(bm);
1175 {
1176 DBG("%s fail %d\n", __FUNCTION__, bm->fail);
1177 assert(intel->locked);
1178
1179 if (!bm->fail) {
1180 struct block *block, *tmp;
1181
1182 foreach_s(block, tmp, &bm->referenced) {
1183 struct buffer *buf = block->buf;
1184
1185 DBG("Validate buf %d / block %p / dirty %d\n", buf->id, block, buf->dirty);
1186
1187 /* Upload the buffer contents if necessary:
1188 */
1189 if (buf->dirty) {
1190 DBG("Upload dirty buf %d (%s) sz %d offset 0x%x\n", buf->id,
1191 buf->name, buf->size, block->mem->ofs);
1192
1193 assert(!(buf->flags & (BM_NO_BACKING_STORE|BM_NO_EVICT)));
1194
1195 wait_quiescent(intel, buf->block);
1196
1197 do_memcpy(buf->block->virtual,
1198 buf->backing_store,
1199 buf->size);
1200
1201 if (intel->aub_file) {
1202 intel->vtbl.aub_gtt_data(intel,
1203 buf->block->mem->ofs,
1204 buf->backing_store,
1205 buf->size,
1206 0,
1207 0);
1208 }
1209
1210 buf->dirty = 0;
1211 buf->aub_dirty = 0;
1212 }
1213 else if (buf->aub_dirty) {
1214 intel->vtbl.aub_gtt_data(intel,
1215 buf->block->mem->ofs,
1216 buf->block->virtual,
1217 buf->size,
1218 0,
1219 0);
1220 buf->aub_dirty = 0;
1221 }
1222
1223 block->referenced = 0;
1224 block->on_hardware = 1;
1225 move_to_tail(&bm->on_hardware, block);
1226 }
1227
1228 bm->need_fence = 1;
1229 }
1230
1231 retval = bm->fail ? -1 : 0;
1232 }
1233 UNLOCK(bm);
1234
1235
1236 if (retval != 0)
1237 DBG("%s failed\n", __FUNCTION__);
1238
1239 return retval;
1240 }
1241
1242
1243
1244
1245 void bmReleaseBuffers( struct intel_context *intel )
1246 {
1247 struct bufmgr *bm = intel->bm;
1248
1249 LOCK(bm);
1250 {
1251 struct block *block, *tmp;
1252
1253 foreach_s (block, tmp, &bm->referenced) {
1254
1255 DBG("remove block %p from referenced list\n", block);
1256
1257 if (block->on_hardware) {
1258 /* Return to the on-hardware list.
1259 */
1260 move_to_tail(&bm->on_hardware, block);
1261 }
1262 else if (block->fenced) {
1263 struct block *s;
1264
1265 /* Hmm - have to scan the fenced list to insert the
1266 * buffers in order. This is O(nm), but rare and the
1267 * numbers are low.
1268 */
1269 foreach (s, &bm->fenced) {
1270 if (FENCE_LTE(block->fence, s->fence))
1271 break;
1272 }
1273
1274 move_to_tail(s, block);
1275 }
1276 else {
1277 /* Return to the lru list:
1278 */
1279 move_to_tail(&block->pool->lru, block);
1280 }
1281
1282 block->referenced = 0;
1283 }
1284 }
1285 UNLOCK(bm);
1286 }
1287
1288
1289 /* This functionality is used by the buffer manager, not really sure
1290 * if we need to be exposing it in this way, probably libdrm will
1291 * offer equivalent calls.
1292 *
1293 * For now they can stay, but will likely change/move before final:
1294 */
1295 unsigned bmSetFence( struct intel_context *intel )
1296 {
1297 assert(intel->locked);
1298
1299 /* Emit MI_FLUSH here:
1300 */
1301 if (intel->bm->need_fence) {
1302
1303 /* Emit a flush without using a batchbuffer. Can't rely on the
1304 * batchbuffer at this level really. Would really prefer that
1305 * the IRQ ioctly emitted the flush at the same time.
1306 */
1307 GLuint dword[2];
1308 dword[0] = intel->vtbl.flush_cmd();
1309 dword[1] = 0;
1310 intel_cmd_ioctl(intel, (char *)&dword, sizeof(dword), GL_TRUE);
1311
1312 intel->bm->last_fence = intelEmitIrqLocked( intel );
1313
1314 fence_blocks(intel, intel->bm->last_fence);
1315
1316 intel->vtbl.note_fence(intel, intel->bm->last_fence);
1317 intel->bm->need_fence = 0;
1318
1319 if (intel->thrashing) {
1320 intel->thrashing--;
1321 if (!intel->thrashing)
1322 DBG("not thrashing\n");
1323 }
1324
1325 intel->bm->free_on_hardware = 0;
1326 }
1327
1328 return intel->bm->last_fence;
1329 }
1330
1331 unsigned bmLockAndFence( struct intel_context *intel )
1332 {
1333 if (intel->bm->need_fence) {
1334 LOCK_HARDWARE(intel);
1335 bmSetFence(intel);
1336 UNLOCK_HARDWARE(intel);
1337 }
1338
1339 return intel->bm->last_fence;
1340 }
1341
1342
1343 void bmFinishFence( struct intel_context *intel, unsigned fence )
1344 {
1345 if (!bmTestFence(intel, fence)) {
1346 DBG("...wait on fence %d\n", fence);
1347 intelWaitIrq( intel, fence );
1348 }
1349 assert(bmTestFence(intel, fence));
1350 check_fenced(intel);
1351 }
1352
1353
1354
1355
1356 /* Specifically ignore texture memory sharing.
1357 * -- just evict everything
1358 * -- and wait for idle
1359 */
1360 void bm_fake_NotifyContendedLockTake( struct intel_context *intel )
1361 {
1362 struct bufmgr *bm = intel->bm;
1363
1364 LOCK(bm);
1365 {
1366 struct block *block, *tmp;
1367 GLuint i;
1368
1369 assert(is_empty_list(&bm->referenced));
1370
1371 bm->need_fence = 1;
1372 bm->fail = 0;
1373 bmFinishFence(intel, bmSetFence(intel));
1374
1375 assert(is_empty_list(&bm->fenced));
1376 assert(is_empty_list(&bm->on_hardware));
1377
1378 for (i = 0; i < bm->nr_pools; i++) {
1379 if (!(bm->pool[i].flags & BM_NO_EVICT)) {
1380 foreach_s(block, tmp, &bm->pool[i].lru) {
1381 assert(bmTestFence(intel, block->fence));
1382 set_dirty(intel, block->buf);
1383 }
1384 }
1385 }
1386 }
1387 UNLOCK(bm);
1388 }
1389
1390
1391
1392 void bmEvictAll( struct intel_context *intel )
1393 {
1394 struct bufmgr *bm = intel->bm;
1395
1396 LOCK(bm);
1397 {
1398 struct block *block, *tmp;
1399 GLuint i;
1400
1401 DBG("%s\n", __FUNCTION__);
1402
1403 assert(is_empty_list(&bm->referenced));
1404
1405 bm->need_fence = 1;
1406 bm->fail = 0;
1407 bmFinishFence(intel, bmSetFence(intel));
1408
1409 assert(is_empty_list(&bm->fenced));
1410 assert(is_empty_list(&bm->on_hardware));
1411
1412 for (i = 0; i < bm->nr_pools; i++) {
1413 if (!(bm->pool[i].flags & BM_NO_EVICT)) {
1414 foreach_s(block, tmp, &bm->pool[i].lru) {
1415 assert(bmTestFence(intel, block->fence));
1416 set_dirty(intel, block->buf);
1417 block->buf->block = NULL;
1418
1419 free_block(intel, block);
1420 }
1421 }
1422 }
1423 }
1424 UNLOCK(bm);
1425 }
1426
1427
1428 GLboolean bmError( struct intel_context *intel )
1429 {
1430 struct bufmgr *bm = intel->bm;
1431 GLboolean retval;
1432
1433 LOCK(bm);
1434 {
1435 retval = bm->fail;
1436 }
1437 UNLOCK(bm);
1438
1439 return retval;
1440 }
1441
1442
1443 GLuint bmCtxId( struct intel_context *intel )
1444 {
1445 return intel->bm->ctxId;
1446 }