1 /**************************************************************************
3 * Copyright 2006 Tungsten Graphics, Inc., Cedar Park, Texas.
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:
14 * The above copyright notice and this permission notice (including the
15 * next paragraph) shall be included in all copies or substantial portions
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.
26 **************************************************************************/
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.
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.
38 #include "intel_context.h"
39 #include "intel_ioctl.h"
40 #include "intel_batchbuffer.h"
42 #include "simple_list.h"
50 #define BM_NO_BACKING_STORE 0x2000
51 #define BM_NO_FENCE_SUBDATA 0x4000
54 static int check_fenced( struct intel_context
*intel
);
56 static int nr_attach
= 0;
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
65 struct block
*next
, *prev
;
66 struct pool
*pool
; /* BM_MEM_AGP */
67 struct mem_block
*mem
; /* BM_MEM_AGP */
69 unsigned referenced
:1;
70 unsigned on_hardware
:1;
74 unsigned fence
; /* BM_MEM_AGP, Split to read_fence, write_fence */
82 unsigned id
; /* debug only */
89 unsigned alignment
:13;
94 void (*invalidate_cb
)( struct intel_context
*, void * );
101 struct buffer
*static_buffer
;
103 struct mem_block
*heap
;
105 struct block lru
; /* only allocated, non-fence-pending blocks here */
109 _glthread_Mutex mutex
; /**< for thread safety */
110 struct pool pool
[BM_POOL_MAX
];
113 unsigned buf_nr
; /* for generating ids */
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() */
121 unsigned free_on_hardware
;
124 unsigned need_fence
:1;
127 #define MAXFENCE 0x7fffffff
129 static GLboolean
FENCE_LTE( unsigned a
, unsigned b
)
134 if (a
< b
&& b
- a
< (1<<24))
137 if (a
> b
&& MAXFENCE
- a
+ b
< (1<<24))
143 int bmTestFence( struct intel_context
*intel
, unsigned fence
)
145 /* Slight problem with wrap-around:
147 return fence
== 0 || FENCE_LTE(fence
, intel
->sarea
->last_dispatch
);
151 int dolock = nr_attach > 1; \
152 if (dolock) _glthread_LOCK_MUTEX(bm->mutex)
155 if (dolock) _glthread_UNLOCK_MUTEX(bm->mutex)
159 static GLboolean
alloc_from_pool( struct intel_context
*intel
,
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 GLuint sz
, align
= (1<<buf
->alignment
);
171 sz
= (buf
->size
+ align
-1) & ~(align
-1);
173 block
->mem
= mmAllocMem(pool
->heap
,
181 make_empty_list(block
);
183 /* Insert at head or at tail???
185 insert_at_tail(&pool
->lru
, block
);
188 block
->virtual = pool
->virtual + block
->mem
->ofs
;
203 /* Release the card storage associated with buf:
205 static void free_block( struct intel_context
*intel
, struct block
*block
)
207 DBG("free block %p\n", block
);
214 if (block
->referenced
) {
215 _mesa_printf("tried to free block on referenced list\n");
218 else if (block
->on_hardware
) {
220 intel
->bm
->free_on_hardware
+= block
->mem
->size
;
222 else if (block
->fenced
) {
226 DBG(" - free immediately\n");
227 remove_from_list(block
);
229 mmFreeMem(block
->mem
);
235 static void alloc_backing_store( struct intel_context
*intel
, struct buffer
*buf
)
237 assert(!buf
->backing_store
);
238 assert(!(buf
->flags
& (BM_NO_EVICT
|BM_NO_BACKING_STORE
)));
240 buf
->backing_store
= ALIGN_MALLOC(buf
->size
, 64);
243 static void free_backing_store( struct intel_context
*intel
, struct buffer
*buf
)
245 assert(!(buf
->flags
& (BM_NO_EVICT
|BM_NO_BACKING_STORE
)));
247 if (buf
->backing_store
) {
248 ALIGN_FREE(buf
->backing_store
);
249 buf
->backing_store
= NULL
;
258 static void set_dirty( struct intel_context
*intel
,
261 if (buf
->flags
& BM_NO_BACKING_STORE
)
262 buf
->invalidate_cb(intel
, buf
->invalidate_ptr
);
264 assert(!(buf
->flags
& BM_NO_EVICT
));
266 DBG("set_dirty - buf %d\n", buf
->id
);
271 static int evict_lru( struct intel_context
*intel
, GLuint max_fence
, GLuint
*pool
)
273 struct bufmgr
*bm
= intel
->bm
;
274 struct block
*block
, *tmp
;
277 DBG("%s\n", __FUNCTION__
);
279 for (i
= 0; i
< bm
->nr_pools
; i
++) {
280 if (!(bm
->pool
[i
].flags
& BM_NO_EVICT
)) {
281 foreach_s(block
, tmp
, &bm
->pool
[i
].lru
) {
284 (block
->buf
->flags
& BM_NO_FENCE_SUBDATA
))
287 if (block
->fence
&& max_fence
&&
288 !FENCE_LTE(block
->fence
, max_fence
))
291 set_dirty(intel
, block
->buf
);
292 block
->buf
->block
= NULL
;
294 free_block(intel
, block
);
306 #define foreach_s_rev(ptr, t, list) \
307 for(ptr=(list)->prev,t=(ptr)->prev; list != ptr; ptr=t, t=(t)->prev)
309 static int evict_mru( struct intel_context
*intel
, GLuint
*pool
)
311 struct bufmgr
*bm
= intel
->bm
;
312 struct block
*block
, *tmp
;
315 DBG("%s\n", __FUNCTION__
);
317 for (i
= 0; i
< bm
->nr_pools
; i
++) {
318 if (!(bm
->pool
[i
].flags
& BM_NO_EVICT
)) {
319 foreach_s_rev(block
, tmp
, &bm
->pool
[i
].lru
) {
322 (block
->buf
->flags
& BM_NO_FENCE_SUBDATA
))
325 set_dirty(intel
, block
->buf
);
326 block
->buf
->block
= NULL
;
328 free_block(intel
, block
);
341 static int check_fenced( struct intel_context
*intel
)
343 struct bufmgr
*bm
= intel
->bm
;
344 struct block
*block
, *tmp
;
347 foreach_s(block
, tmp
, &bm
->fenced
) {
348 assert(block
->fenced
);
350 if (bmTestFence(intel
, block
->fence
)) {
355 DBG("delayed free: offset %x sz %x\n", block
->mem
->ofs
, block
->mem
->size
);
356 remove_from_list(block
);
357 mmFreeMem(block
->mem
);
361 DBG("return to lru: offset %x sz %x\n", block
->mem
->ofs
, block
->mem
->size
);
362 move_to_tail(&block
->pool
->lru
, block
);
368 /* Blocks are ordered by fence, so if one fails, all from
369 * here will fail also:
375 /* Also check the referenced list:
377 foreach_s(block
, tmp
, &bm
->referenced
) {
379 bmTestFence(intel
, block
->fence
)) {
385 DBG("%s: %d\n", __FUNCTION__
, ret
);
391 static void fence_blocks( struct intel_context
*intel
,
394 struct bufmgr
*bm
= intel
->bm
;
395 struct block
*block
, *tmp
;
397 foreach_s (block
, tmp
, &bm
->on_hardware
) {
398 DBG("Fence block %p (sz 0x%x buf %p) with fence %d\n", block
,
399 block
->mem
->size
, block
->buf
, fence
);
400 block
->fence
= fence
;
402 block
->on_hardware
= 0;
405 /* Move to tail of pending list here
407 move_to_tail(&bm
->fenced
, block
);
410 /* Also check the referenced list:
412 foreach_s (block
, tmp
, &bm
->referenced
) {
413 if (block
->on_hardware
) {
414 DBG("Fence block %p (sz 0x%x buf %p) with fence %d\n", block
,
415 block
->mem
->size
, block
->buf
, fence
);
417 block
->fence
= fence
;
418 block
->on_hardware
= 0;
424 bm
->last_fence
= fence
;
425 assert(is_empty_list(&bm
->on_hardware
));
431 static GLboolean
alloc_block( struct intel_context
*intel
,
434 struct bufmgr
*bm
= intel
->bm
;
437 assert(intel
->locked
);
439 DBG("%s 0x%x bytes (%s)\n", __FUNCTION__
, buf
->size
, buf
->name
);
441 for (i
= 0; i
< bm
->nr_pools
; i
++) {
442 if (!(bm
->pool
[i
].flags
& BM_NO_ALLOC
) &&
443 alloc_from_pool(intel
, i
, buf
)) {
445 DBG("%s --> 0x%x (sz %x)\n", __FUNCTION__
,
446 buf
->block
->mem
->ofs
, buf
->block
->mem
->size
);
452 DBG("%s --> fail\n", __FUNCTION__
);
457 static GLboolean
evict_and_alloc_block( struct intel_context
*intel
,
461 struct bufmgr
*bm
= intel
->bm
;
463 assert(buf
->block
== NULL
);
465 /* Put a cap on the amount of free memory we'll allow to accumulate
466 * before emitting a fence.
468 if (bm
->free_on_hardware
> 1 * 1024 * 1024) {
469 DBG("fence for free space: %x\n", bm
->free_on_hardware
);
473 /* Search for already free memory:
475 if (alloc_block(intel
, buf
))
478 /* Look for memory that may have become free:
480 if (check_fenced(intel
) &&
481 alloc_block(intel
, buf
))
484 /* Look for memory blocks not used for >1 frame:
486 while (evict_lru(intel
, intel
->second_last_swap_fence
, &pool
))
487 if (alloc_from_pool(intel
, pool
, buf
))
490 /* If we're not thrashing, allow lru eviction to dig deeper into
491 * recently used textures. We'll probably be thrashing soon:
493 if (!intel
->thrashing
) {
494 while (evict_lru(intel
, 0, &pool
))
495 if (alloc_from_pool(intel
, pool
, buf
))
499 /* Keep thrashing counter alive?
501 if (intel
->thrashing
)
502 intel
->thrashing
= 20;
504 /* Wait on any already pending fences - here we are waiting for any
505 * freed memory that has been submitted to hardware and fenced to
508 while (!is_empty_list(&bm
->fenced
)) {
509 GLuint fence
= bm
->fenced
.next
->fence
;
510 bmFinishFence(intel
, fence
);
512 if (alloc_block(intel
, buf
))
519 if (!is_empty_list(&bm
->on_hardware
)) {
522 while (!is_empty_list(&bm
->fenced
)) {
523 GLuint fence
= bm
->fenced
.next
->fence
;
524 bmFinishFence(intel
, fence
);
527 if (!intel
->thrashing
) {
530 intel
->thrashing
= 20;
532 if (alloc_block(intel
, buf
))
536 while (evict_mru(intel
, &pool
))
537 if (alloc_from_pool(intel
, pool
, buf
))
540 DBG("%s 0x%x bytes failed\n", __FUNCTION__
, buf
->size
);
542 assert(is_empty_list(&bm
->on_hardware
));
543 assert(is_empty_list(&bm
->fenced
));
557 /***********************************************************************
562 /* The initialization functions are skewed in the fake implementation.
563 * This call would be to attach to an existing manager, rather than to
564 * create a local one.
566 struct bufmgr
*bm_fake_intel_Attach( struct intel_context
*intel
)
568 _glthread_DECLARE_STATIC_MUTEX(initMutex
);
569 static struct bufmgr bm
;
571 /* This function needs a mutex of its own...
573 _glthread_LOCK_MUTEX(initMutex
);
575 if (nr_attach
== 0) {
576 _glthread_INIT_MUTEX(bm
.mutex
);
578 make_empty_list(&bm
.referenced
);
579 make_empty_list(&bm
.fenced
);
580 make_empty_list(&bm
.on_hardware
);
585 _glthread_UNLOCK_MUTEX(initMutex
);
592 /* The virtual pointer would go away in a true implementation.
594 int bmInitPool( struct intel_context
*intel
,
595 unsigned long low_offset
,
600 struct bufmgr
*bm
= intel
->bm
;
607 for (i
= 0; i
< bm
->nr_pools
; i
++) {
608 if (bm
->pool
[i
].low_offset
== low_offset
&&
609 bm
->pool
[i
].size
== size
) {
616 if (bm
->nr_pools
>= BM_POOL_MAX
)
621 DBG("bmInitPool %d low_offset %x sz %x\n",
622 i
, low_offset
, size
);
624 bm
->pool
[i
].low_offset
= low_offset
;
625 bm
->pool
[i
].size
= size
;
626 bm
->pool
[i
].heap
= mmInit( low_offset
, size
);
627 bm
->pool
[i
].virtual = low_virtual
- low_offset
;
628 bm
->pool
[i
].flags
= flags
;
630 make_empty_list(&bm
->pool
[i
].lru
);
640 static struct buffer
*do_GenBuffer(struct intel_context
*intel
, const char *name
, int align
)
642 struct bufmgr
*bm
= intel
->bm
;
643 struct buffer
*buf
= calloc(sizeof(*buf
), 1);
645 buf
->id
= ++bm
->buf_nr
;
647 buf
->alignment
= align
;
648 buf
->flags
= BM_MEM_AGP
|BM_MEM_VRAM
|BM_MEM_LOCAL
;
654 void *bmFindVirtual( struct intel_context
*intel
,
658 struct bufmgr
*bm
= intel
->bm
;
661 for (i
= 0; i
< bm
->nr_pools
; i
++)
662 if (offset
>= bm
->pool
[i
].low_offset
&&
663 offset
+ sz
<= bm
->pool
[i
].low_offset
+ bm
->pool
[i
].size
)
664 return bm
->pool
[i
].virtual + offset
;
670 void bmGenBuffers(struct intel_context
*intel
,
671 const char *name
, unsigned n
,
672 struct buffer
**buffers
,
675 struct bufmgr
*bm
= intel
->bm
;
680 for (i
= 0; i
< n
; i
++)
681 buffers
[i
] = do_GenBuffer(intel
, name
, align
);
687 void bmDeleteBuffers(struct intel_context
*intel
, unsigned n
, struct buffer
**buffers
)
689 struct bufmgr
*bm
= intel
->bm
;
695 for (i
= 0; i
< n
; i
++) {
696 struct buffer
*buf
= buffers
[i
];
698 if (buf
&& buf
->block
)
699 free_block(intel
, buf
->block
);
711 /* Hook to inform faked buffer manager about fixed-position
712 * front,depth,back buffers. These may move to a fully memory-managed
713 * scheme, or they may continue to be managed as is. It will probably
714 * be useful to pass a fixed offset here one day.
716 struct buffer
*bmGenBufferStatic(struct intel_context
*intel
,
719 struct bufmgr
*bm
= intel
->bm
;
723 assert(bm
->pool
[pool
].flags
& BM_NO_EVICT
);
724 assert(bm
->pool
[pool
].flags
& BM_NO_MOVE
);
726 if (bm
->pool
[pool
].static_buffer
)
727 buf
= bm
->pool
[pool
].static_buffer
;
729 buf
= do_GenBuffer(intel
, "static", 12);
731 bm
->pool
[pool
].static_buffer
= buf
;
734 buf
->size
= bm
->pool
[pool
].size
;
735 buf
->flags
= bm
->pool
[pool
].flags
;
738 if (!alloc_from_pool(intel
, pool
, buf
))
747 static void wait_quiescent(struct intel_context
*intel
,
750 if (block
->on_hardware
) {
751 assert(intel
->bm
->need_fence
);
753 assert(!block
->on_hardware
);
758 bmFinishFence(intel
, block
->fence
);
761 assert(!block
->on_hardware
);
762 assert(!block
->fenced
);
767 /* If buffer size changes, free and reallocate. Otherwise update in
770 int bmBufferData(struct intel_context
*intel
,
776 struct bufmgr
*bm
= intel
->bm
;
781 DBG("bmBufferData %d sz 0x%x data: %p\n", buf
->id
, size
, data
);
783 assert(!buf
->mapped
);
786 struct block
*block
= buf
->block
;
788 /* Optimistic check to see if we can reuse the block -- not
789 * required for correctness:
794 if (block
->on_hardware
||
796 (buf
->size
&& buf
->size
!= size
) ||
799 assert(!block
->referenced
);
801 free_block(intel
, block
);
809 assert (buf
->block
->mem
->size
>= size
);
812 if (buf
->flags
& (BM_NO_BACKING_STORE
|BM_NO_EVICT
)) {
814 assert(intel
->locked
|| data
== NULL
);
817 if (!buf
->block
&& !evict_and_alloc_block(intel
, buf
)) {
823 wait_quiescent(intel
, buf
->block
);
825 DBG("bmBufferData %d offset 0x%x sz 0x%x\n",
826 buf
->id
, buf
->block
->mem
->ofs
, size
);
828 assert(buf
->block
->virtual == buf
->block
->pool
->virtual + buf
->block
->mem
->ofs
);
830 do_memcpy(buf
->block
->virtual, data
, size
);
835 DBG("%s - set buf %d dirty\n", __FUNCTION__
, buf
->id
);
836 set_dirty(intel
, buf
);
837 free_backing_store(intel
, buf
);
840 alloc_backing_store(intel
, buf
);
841 do_memcpy(buf
->backing_store
, data
, size
);
851 /* Update the buffer in place, in whatever space it is currently resident:
853 int bmBufferSubData(struct intel_context
*intel
,
859 struct bufmgr
*bm
= intel
->bm
;
867 DBG("bmBufferSubdata %d offset 0x%x sz 0x%x\n", buf
->id
, offset
, size
);
869 assert(offset
+size
<= buf
->size
);
871 if (buf
->flags
& (BM_NO_EVICT
|BM_NO_BACKING_STORE
)) {
873 assert(intel
->locked
);
875 if (!buf
->block
&& !evict_and_alloc_block(intel
, buf
)) {
881 if (!(buf
->flags
& BM_NO_FENCE_SUBDATA
))
882 wait_quiescent(intel
, buf
->block
);
886 do_memcpy(buf
->block
->virtual + offset
, data
, size
);
889 DBG("%s - set buf %d dirty\n", __FUNCTION__
, buf
->id
);
890 set_dirty(intel
, buf
);
892 if (buf
->backing_store
== NULL
)
893 alloc_backing_store(intel
, buf
);
895 do_memcpy(buf
->backing_store
+ offset
, data
, size
);
905 int bmBufferDataAUB(struct intel_context
*intel
,
911 unsigned aubsubtype
)
913 int retval
= bmBufferData(intel
, buf
, size
, data
, flags
);
916 /* This only works because in this version of the buffer manager we
917 * allocate all buffers statically in agp space and so can emit the
918 * uploads to the aub file with the correct offsets as they happen.
920 if (retval
== 0 && data
&& intel
->aub_file
) {
922 if (buf
->block
&& !buf
->dirty
) {
923 intel
->vtbl
.aub_gtt_data(intel
,
924 buf
->block
->mem
->ofs
,
937 int bmBufferSubDataAUB(struct intel_context
*intel
,
943 unsigned aubsubtype
)
945 int retval
= bmBufferSubData(intel
, buf
, offset
, size
, data
);
948 /* This only works because in this version of the buffer manager we
949 * allocate all buffers statically in agp space and so can emit the
950 * uploads to the aub file with the correct offsets as they happen.
952 if (intel
->aub_file
) {
953 if (retval
== 0 && buf
->block
&& !buf
->dirty
)
954 intel
->vtbl
.aub_gtt_data(intel
,
955 buf
->block
->mem
->ofs
+ offset
,
956 ((const char *)buf
->block
->virtual) + offset
,
965 void bmUnmapBufferAUB( struct intel_context
*intel
,
968 unsigned aubsubtype
)
970 bmUnmapBuffer(intel
, buf
);
972 if (intel
->aub_file
) {
973 /* Hack - exclude the framebuffer mappings. If you removed
974 * this, you'd get very big aubfiles, but you *would* be able to
975 * see fallback rendering.
977 if (buf
->block
&& !buf
->dirty
&& buf
->block
->pool
== &intel
->bm
->pool
[0]) {
983 unsigned bmBufferOffset(struct intel_context
*intel
,
986 struct bufmgr
*bm
= intel
->bm
;
991 assert(intel
->locked
);
994 !evict_and_alloc_block(intel
, buf
)) {
1000 assert(buf
->block
->buf
== buf
);
1002 DBG("Add buf %d (block %p, dirty %d) to referenced list\n", buf
->id
, buf
->block
,
1005 move_to_tail(&bm
->referenced
, buf
->block
);
1006 buf
->block
->referenced
= 1;
1008 retval
= buf
->block
->mem
->ofs
;
1018 /* Extract data from the buffer:
1020 void bmBufferGetSubData(struct intel_context
*intel
,
1026 struct bufmgr
*bm
= intel
->bm
;
1030 DBG("bmBufferSubdata %d offset 0x%x sz 0x%x\n", buf
->id
, offset
, size
);
1032 if (buf
->flags
& (BM_NO_EVICT
|BM_NO_BACKING_STORE
)) {
1033 if (buf
->block
&& size
) {
1034 wait_quiescent(intel
, buf
->block
);
1035 do_memcpy(data
, buf
->block
->virtual + offset
, size
);
1039 if (buf
->backing_store
&& size
) {
1040 do_memcpy(data
, buf
->backing_store
+ offset
, size
);
1048 /* Return a pointer to whatever space the buffer is currently resident in:
1050 void *bmMapBuffer( struct intel_context
*intel
,
1054 struct bufmgr
*bm
= intel
->bm
;
1055 void *retval
= NULL
;
1059 DBG("bmMapBuffer %d\n", buf
->id
);
1062 _mesa_printf("%s: already mapped\n", __FUNCTION__
);
1065 else if (buf
->flags
& (BM_NO_BACKING_STORE
|BM_NO_EVICT
)) {
1067 assert(intel
->locked
);
1069 if (!buf
->block
&& !evict_and_alloc_block(intel
, buf
)) {
1070 DBG("%s: alloc failed\n", __FUNCTION__
);
1078 if (!(buf
->flags
& BM_NO_FENCE_SUBDATA
))
1079 wait_quiescent(intel
, buf
->block
);
1082 retval
= buf
->block
->virtual;
1086 DBG("%s - set buf %d dirty\n", __FUNCTION__
, buf
->id
);
1087 set_dirty(intel
, buf
);
1089 if (buf
->backing_store
== 0)
1090 alloc_backing_store(intel
, buf
);
1093 retval
= buf
->backing_store
;
1100 void bmUnmapBuffer( struct intel_context
*intel
, struct buffer
*buf
)
1102 struct bufmgr
*bm
= intel
->bm
;
1106 DBG("bmUnmapBuffer %d\n", buf
->id
);
1115 /* This is the big hack that turns on BM_NO_BACKING_STORE. Basically
1116 * says that an external party will maintain the backing store, eg
1117 * Mesa's local copy of texture data.
1119 void bmBufferSetInvalidateCB(struct intel_context
*intel
,
1121 void (*invalidate_cb
)( struct intel_context
*, void *ptr
),
1123 GLboolean dont_fence_subdata
)
1125 struct bufmgr
*bm
= intel
->bm
;
1129 if (buf
->backing_store
)
1130 free_backing_store(intel
, buf
);
1132 buf
->flags
|= BM_NO_BACKING_STORE
;
1134 if (dont_fence_subdata
)
1135 buf
->flags
|= BM_NO_FENCE_SUBDATA
;
1137 DBG("bmBufferSetInvalidateCB set buf %d dirty\n", buf
->id
);
1139 buf
->invalidate_cb
= invalidate_cb
;
1140 buf
->invalidate_ptr
= ptr
;
1142 /* Note that it is invalid right from the start. Also note
1143 * invalidate_cb is called with the bufmgr locked, so cannot
1144 * itself make bufmgr calls.
1146 invalidate_cb( intel
, ptr
);
1157 /* This is only protected against thread interactions by the DRI lock
1158 * and the policy of ensuring that all dma is flushed prior to
1159 * releasing that lock. Otherwise you might have two threads building
1160 * up a list of buffers to validate at once.
1162 int bmValidateBuffers( struct intel_context
*intel
)
1164 struct bufmgr
*bm
= intel
->bm
;
1169 DBG("%s fail %d\n", __FUNCTION__
, bm
->fail
);
1170 assert(intel
->locked
);
1173 struct block
*block
, *tmp
;
1175 foreach_s(block
, tmp
, &bm
->referenced
) {
1176 struct buffer
*buf
= block
->buf
;
1178 DBG("Validate buf %d / block %p / dirty %d\n", buf
->id
, block
, buf
->dirty
);
1180 /* Upload the buffer contents if necessary:
1183 DBG("Upload dirty buf %d (%s) sz %d offset 0x%x\n", buf
->id
,
1184 buf
->name
, buf
->size
, block
->mem
->ofs
);
1186 assert(!(buf
->flags
& (BM_NO_BACKING_STORE
|BM_NO_EVICT
)));
1188 wait_quiescent(intel
, buf
->block
);
1190 do_memcpy(buf
->block
->virtual,
1194 if (intel
->aub_file
) {
1195 intel
->vtbl
.aub_gtt_data(intel
,
1196 buf
->block
->mem
->ofs
,
1206 else if (buf
->aub_dirty
) {
1207 intel
->vtbl
.aub_gtt_data(intel
,
1208 buf
->block
->mem
->ofs
,
1209 buf
->block
->virtual,
1216 block
->referenced
= 0;
1217 block
->on_hardware
= 1;
1218 move_to_tail(&bm
->on_hardware
, block
);
1224 retval
= bm
->fail
? -1 : 0;
1230 DBG("%s failed\n", __FUNCTION__
);
1238 void bmReleaseBuffers( struct intel_context
*intel
)
1240 struct bufmgr
*bm
= intel
->bm
;
1244 struct block
*block
, *tmp
;
1245 assert(intel
->locked
);
1247 foreach_s (block
, tmp
, &bm
->referenced
) {
1249 DBG("remove block %p from referenced list\n", block
);
1251 if (block
->on_hardware
) {
1252 /* Return to the on-hardware list.
1254 move_to_tail(&bm
->on_hardware
, block
);
1256 else if (block
->fenced
) {
1259 /* Hmm - have to scan the fenced list to insert the
1260 * buffers in order. This is O(nm), but rare and the
1263 foreach (s
, &bm
->fenced
) {
1264 if (FENCE_LTE(block
->fence
, s
->fence
))
1268 move_to_tail(s
, block
);
1271 /* Return to the lru list:
1273 move_to_tail(&block
->pool
->lru
, block
);
1276 block
->referenced
= 0;
1283 /* This functionality is used by the buffer manager, not really sure
1284 * if we need to be exposing it in this way, probably libdrm will
1285 * offer equivalent calls.
1287 * For now they can stay, but will likely change/move before final:
1289 unsigned bmSetFence( struct intel_context
*intel
)
1291 assert(intel
->locked
);
1293 /* Emit MI_FLUSH here:
1295 if (intel
->bm
->need_fence
) {
1297 /* Emit a flush without using a batchbuffer. Can't rely on the
1298 * batchbuffer at this level really. Would really prefer that
1299 * the IRQ ioctly emitted the flush at the same time.
1302 dword
[0] = intel
->vtbl
.flush_cmd();
1304 intel_cmd_ioctl(intel
, (char *)&dword
, sizeof(dword
), GL_TRUE
);
1306 intel
->bm
->last_fence
= intelEmitIrqLocked( intel
);
1308 fence_blocks(intel
, intel
->bm
->last_fence
);
1310 intel
->vtbl
.note_fence(intel
, intel
->bm
->last_fence
);
1311 intel
->bm
->need_fence
= 0;
1313 if (intel
->thrashing
) {
1315 if (!intel
->thrashing
)
1316 DBG("not thrashing\n");
1319 intel
->bm
->free_on_hardware
= 0;
1322 return intel
->bm
->last_fence
;
1325 unsigned bmLockAndFence( struct intel_context
*intel
)
1327 if (intel
->bm
->need_fence
) {
1328 LOCK_HARDWARE(intel
);
1330 UNLOCK_HARDWARE(intel
);
1333 return intel
->bm
->last_fence
;
1337 void bmFinishFence( struct intel_context
*intel
, unsigned fence
)
1339 if (!bmTestFence(intel
, fence
)) {
1340 DBG("...wait on fence %d\n", fence
);
1341 intelWaitIrq( intel
, fence
);
1343 assert(bmTestFence(intel
, fence
));
1344 check_fenced(intel
);
1350 /* Specifically ignore texture memory sharing.
1351 * -- just evict everything
1352 * -- and wait for idle
1354 void bm_fake_NotifyContendedLockTake( struct intel_context
*intel
)
1356 struct bufmgr
*bm
= intel
->bm
;
1360 struct block
*block
, *tmp
;
1363 assert(is_empty_list(&bm
->referenced
));
1367 bmFinishFence(intel
, bmSetFence(intel
));
1369 assert(is_empty_list(&bm
->fenced
));
1370 assert(is_empty_list(&bm
->on_hardware
));
1372 for (i
= 0; i
< bm
->nr_pools
; i
++) {
1373 if (!(bm
->pool
[i
].flags
& BM_NO_EVICT
)) {
1374 foreach_s(block
, tmp
, &bm
->pool
[i
].lru
) {
1375 assert(bmTestFence(intel
, block
->fence
));
1376 set_dirty(intel
, block
->buf
);
1386 void bmEvictAll( struct intel_context
*intel
)
1388 struct bufmgr
*bm
= intel
->bm
;
1392 struct block
*block
, *tmp
;
1395 DBG("%s\n", __FUNCTION__
);
1397 assert(is_empty_list(&bm
->referenced
));
1401 bmFinishFence(intel
, bmSetFence(intel
));
1403 assert(is_empty_list(&bm
->fenced
));
1404 assert(is_empty_list(&bm
->on_hardware
));
1406 for (i
= 0; i
< bm
->nr_pools
; i
++) {
1407 if (!(bm
->pool
[i
].flags
& BM_NO_EVICT
)) {
1408 foreach_s(block
, tmp
, &bm
->pool
[i
].lru
) {
1409 assert(bmTestFence(intel
, block
->fence
));
1410 set_dirty(intel
, block
->buf
);
1411 block
->buf
->block
= NULL
;
1413 free_block(intel
, block
);
1422 GLboolean
bmError( struct intel_context
*intel
)
1424 struct bufmgr
*bm
= intel
->bm
;