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