2 * Copyright 2000-2001 VA Linux Systems, Inc.
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * on the rights to use, copy, modify, merge, publish, distribute, sub
9 * license, and/or sell copies of the Software, and to permit persons to whom
10 * the Software is furnished to do so, subject to the following conditions:
12 * The above copyright notice and this permission notice (including the next
13 * paragraph) shall be included in all copies or substantial portions of the
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
19 * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22 * OTHER DEALINGS IN THE SOFTWARE.
25 * Keith Whitwell <keith@tungstengraphics.com>
27 /* $XFree86: xc/lib/GL/mesa/src/drv/mga/mgatexmem.c,v 1.7 2002/10/30 12:51:36 alanh Exp $ */
34 #include "mgacontext.h"
40 #include "simple_list.h"
43 mgaSwapOutTexObj(mgaContextPtr mmesa
, mgaTextureObjectPtr t
)
46 mmFreeMem(t
->MemBlock
);
49 if (t
->age
> mmesa
->dirtyAge
)
50 mmesa
->dirtyAge
= t
->age
;
54 move_to_tail(&(mmesa
->SwappedOut
), t
);
58 mgaPrintLocalLRU( mgaContextPtr mmesa
, int heap
)
60 mgaTextureObjectPtr t
;
61 int sz
= 1 << (mmesa
->mgaScreen
->logTextureGranularity
[heap
]);
63 fprintf(stderr
, "\nLocal LRU, heap %d:\n", heap
);
65 foreach( t
, &(mmesa
->TexObjList
[heap
]) ) {
67 fprintf(stderr
, "Placeholder %d at %x sz %x\n",
68 t
->MemBlock
->ofs
/ sz
,
72 fprintf(stderr
, "Texture (bound %d) at %x sz %x\n",
78 fprintf(stderr
, "\n\n");
82 mgaPrintGlobalLRU( mgaContextPtr mmesa
, int heap
)
85 drmTextureRegion
*list
= mmesa
->sarea
->texList
[heap
];
87 fprintf(stderr
, "\nGlobal LRU, heap %d list %p:\n", heap
, list
);
89 for (i
= 0, j
= MGA_NR_TEX_REGIONS
; i
< MGA_NR_TEX_REGIONS
; i
++) {
90 fprintf(stderr
, "list[%d] age %d next %d prev %d\n",
91 j
, list
[j
].age
, list
[j
].next
, list
[j
].prev
);
93 if (j
== MGA_NR_TEX_REGIONS
) break;
96 if (j
!= MGA_NR_TEX_REGIONS
) {
97 fprintf(stderr
, "Loop detected in global LRU\n\n\n");
98 for (i
= 0 ; i
< MGA_NR_TEX_REGIONS
; i
++) {
99 fprintf(stderr
, "list[%d] age %d next %d prev %d\n",
100 i
, list
[i
].age
, list
[i
].next
, list
[i
].prev
);
104 fprintf(stderr
, "\n\n");
108 static void mgaResetGlobalLRU( mgaContextPtr mmesa
, GLuint heap
)
110 drmTextureRegion
*list
= mmesa
->sarea
->texList
[heap
];
111 int sz
= 1 << mmesa
->mgaScreen
->logTextureGranularity
[heap
];
114 mmesa
->texAge
[heap
] = ++mmesa
->sarea
->texAge
[heap
];
116 if (0) fprintf(stderr
, "mgaResetGlobalLRU %d\n", (int)heap
);
118 /* (Re)initialize the global circular LRU list. The last element
119 * in the array (MGA_NR_TEX_REGIONS) is the sentinal. Keeping it
120 * at the end of the array allows it to be addressed rationally
121 * when looking up objects at a particular location in texture
124 for (i
= 0 ; (i
+1) * sz
<= mmesa
->mgaScreen
->textureSize
[heap
] ; i
++) {
127 list
[i
].age
= mmesa
->sarea
->texAge
[heap
];
131 list
[0].prev
= MGA_NR_TEX_REGIONS
;
133 list
[i
].next
= MGA_NR_TEX_REGIONS
;
134 list
[MGA_NR_TEX_REGIONS
].prev
= i
;
135 list
[MGA_NR_TEX_REGIONS
].next
= 0;
140 static void mgaUpdateTexLRU( mgaContextPtr mmesa
, mgaTextureObjectPtr t
)
144 int logsz
= mmesa
->mgaScreen
->logTextureGranularity
[heap
];
145 int start
= t
->MemBlock
->ofs
>> logsz
;
146 int end
= (t
->MemBlock
->ofs
+ t
->MemBlock
->size
- 1) >> logsz
;
147 drmTextureRegion
*list
= mmesa
->sarea
->texList
[heap
];
149 mmesa
->texAge
[heap
] = ++mmesa
->sarea
->texAge
[heap
];
152 fprintf(stderr
, "no memblock\n\n");
156 /* Update our local LRU
158 move_to_head( &(mmesa
->TexObjList
[heap
]), t
);
162 fprintf(stderr
, "mgaUpdateTexLRU heap %d list %p\n", heap
, list
);
165 /* Update the global LRU
167 for (i
= start
; i
<= end
; i
++) {
170 list
[i
].age
= mmesa
->texAge
[heap
];
172 /* remove_from_list(i)
174 list
[(unsigned)list
[i
].next
].prev
= list
[i
].prev
;
175 list
[(unsigned)list
[i
].prev
].next
= list
[i
].next
;
177 /* insert_at_head(list, i)
179 list
[i
].prev
= MGA_NR_TEX_REGIONS
;
180 list
[i
].next
= list
[MGA_NR_TEX_REGIONS
].next
;
181 list
[(unsigned)list
[MGA_NR_TEX_REGIONS
].next
].prev
= i
;
182 list
[MGA_NR_TEX_REGIONS
].next
= i
;
186 mgaPrintGlobalLRU(mmesa
, t
->heap
);
187 mgaPrintLocalLRU(mmesa
, t
->heap
);
191 /* Called for every shared texture region which has increased in age
192 * since we last held the lock.
194 * Figures out which of our textures have been ejected by other clients,
195 * and pushes a placeholder texture onto the LRU list to represent
196 * the other client's textures.
198 static void mgaTexturesGone( mgaContextPtr mmesa
,
204 mgaTextureObjectPtr t
, tmp
;
208 foreach_s ( t
, tmp
, &(mmesa
->TexObjList
[heap
]) ) {
210 if (t
->MemBlock
->ofs
>= offset
+ size
||
211 t
->MemBlock
->ofs
+ t
->MemBlock
->size
<= offset
)
217 /* It overlaps - kick it off. Need to hold onto the currently bound
221 mgaSwapOutTexObj( mmesa
, t
);
223 mgaDestroyTexObj( mmesa
, t
);
228 t
= (mgaTextureObjectPtr
) calloc(1, sizeof(*t
));
232 t
->MemBlock
= mmAllocMem( mmesa
->texHeap
[heap
], size
, 0, offset
);
234 fprintf(stderr
, "Couldn't alloc placeholder sz %x ofs %x\n",
235 (int)size
, (int)offset
);
236 mmDumpMemInfo( mmesa
->texHeap
[heap
]);
239 insert_at_head( &(mmesa
->TexObjList
[heap
]), t
);
244 void mgaAgeTextures( mgaContextPtr mmesa
, int heap
)
246 MGASAREAPrivPtr sarea
= mmesa
->sarea
;
247 int sz
= 1 << (mmesa
->mgaScreen
->logTextureGranularity
[heap
]);
250 /* Have to go right round from the back to ensure stuff ends up
251 * LRU in our local list... Fix with a cursor pointer.
253 for (idx
= sarea
->texList
[heap
][MGA_NR_TEX_REGIONS
].prev
;
254 idx
!= MGA_NR_TEX_REGIONS
&& nr
< MGA_NR_TEX_REGIONS
;
255 idx
= sarea
->texList
[heap
][idx
].prev
, nr
++)
257 /* If switching texturing schemes, then the SAREA might not
258 * have been properly cleared, so we need to reset the
259 * global texture LRU.
261 if ( idx
* sz
> mmesa
->mgaScreen
->textureSize
[heap
] ) {
262 nr
= MGA_NR_TEX_REGIONS
;
266 if (sarea
->texList
[heap
][idx
].age
> mmesa
->texAge
[heap
]) {
267 mgaTexturesGone(mmesa
, heap
, idx
* sz
, sz
,
268 sarea
->texList
[heap
][idx
].in_use
);
272 if (nr
== MGA_NR_TEX_REGIONS
) {
273 mgaTexturesGone(mmesa
, heap
, 0,
274 mmesa
->mgaScreen
->textureSize
[heap
], 0);
275 mgaResetGlobalLRU( mmesa
, heap
);
280 mgaPrintGlobalLRU( mmesa
, heap
);
281 mgaPrintLocalLRU( mmesa
, heap
);
284 mmesa
->texAge
[heap
] = sarea
->texAge
[heap
];
285 mmesa
->dirty
|= MGA_UPLOAD_TEX0IMAGE
| MGA_UPLOAD_TEX1IMAGE
;
289 * mgaUploadSubImageLocked
291 * Perform an iload based update of a resident buffer. This is used for
292 * both initial loading of the entire image, and texSubImage updates.
294 * Performed with the hardware lock held.
296 void mgaUploadSubImageLocked( mgaContextPtr mmesa
,
297 mgaTextureObjectPtr t
,
299 int x
, int y
, int width
, int height
)
304 struct gl_texture_image
*image
;
305 int texelBytes
, texelsPerDword
, texelMaccess
, length
;
307 if ( level
< 0 || level
>= MGA_TEX_MAXLEVELS
)
310 image
= t
->tObj
->Image
[level
];
311 if ( !image
) return;
314 if (image
->Data
== 0) {
315 fprintf(stderr
, "null texture image data tObj %p level %d\n",
321 /* find the proper destination offset for this level */
322 offset
= (t
->MemBlock
->ofs
+
326 texelBytes
= t
->texelBytes
;
327 switch( texelBytes
) {
345 /* We can't do a subimage update if pitch is < 32 texels due
346 * to hardware XY addressing limits, so we will need to
347 * linearly upload all modified rows.
349 if ( image
->Width
< 32 ) {
351 width
= image
->Width
* height
;
354 /* Assume that 1x1 textures aren't going to cause a
355 * bus error if we read up to four texels from that
358 /* if ( width < texelsPerDword ) { */
359 /* width = texelsPerDword; */
362 /* pad the size out to dwords. The image is a pointer
363 to the entire image, so we can safely reference
364 outside the x,y,width,height bounds if we need to */
366 x2
= (x2
+ (texelsPerDword
-1)) & ~(texelsPerDword
-1);
367 x
= (x
+ (texelsPerDword
-1)) & ~(texelsPerDword
-1);
371 /* we may not be able to upload the entire texture in one
372 batch due to register limits or dma buffer limits.
373 Recursively split it up. */
375 dwords
= height
* width
/ texelsPerDword
;
376 if ( dwords
* 4 <= MGA_BUFFER_SIZE
) {
380 mgaUploadSubImageLocked( mmesa
, t
, level
, x
, y
,
381 width
, height
>> 1 );
382 y
+= ( height
>> 1 );
383 height
-= ( height
>> 1 );
388 /* Fill in the secondary buffer with properly converted texels
389 * from the mesa buffer. */
390 /* FIXME: the sync for direct copy reduces speed.. */
391 if(t
->heap
== MGA_CARD_HEAP
) {
392 mgaGetILoadBufferLocked( mmesa
);
393 mgaConvertTexture( (GLuint
*)mmesa
->iload_buffer
->address
,
394 texelBytes
, image
, x
, y
, width
, height
);
395 if(length
< 64) length
= 64;
398 fprintf(stderr
, "TexelBytes : %d, offset: %d, length : %d\n",
400 mmesa
->mgaScreen
->textureOffset
[t
->heap
] +
402 y
* width
* 4/texelsPerDword
,
405 mgaFireILoadLocked( mmesa
,
406 mmesa
->mgaScreen
->textureOffset
[t
->heap
] +
408 y
* width
* 4/texelsPerDword
,
411 /* This works, is slower for uploads to card space and needs
412 * additional synchronization with the dma stream.
415 UPDATE_LOCK(mmesa
, DRM_LOCK_FLUSH
| DRM_LOCK_QUIESCENT
);
416 mgaConvertTexture( (GLuint
*)
417 (mmesa
->mgaScreen
->texVirtual
[t
->heap
] +
419 y
* width
* 4/texelsPerDword
),
420 texelBytes
, image
, x
, y
, width
, height
);
425 static void mgaUploadTexLevel( mgaContextPtr mmesa
,
426 mgaTextureObjectPtr t
,
429 mgaUploadSubImageLocked( mmesa
,
433 t
->tObj
->Image
[l
]->Width
,
434 t
->tObj
->Image
[l
]->Height
);
441 static void mgaMigrateTexture( mgaContextPtr mmesa
, mgaTextureObjectPtr t
)
448 static int mgaChooseTexHeap( mgaContextPtr mmesa
, mgaTextureObjectPtr t
)
450 int freeagp
, freecard
;
451 int fitincard
, fitinagp
;
452 int totalcard
, totalagp
;
455 totalcard
= totalagp
= fitincard
= fitinagp
= freeagp
= freecard
= 0;
457 b
= mmesa
->texHeap
[0];
460 totalcard
+= b
->size
;
461 if(b
->free
) if(t
->totalSize
<= b
->size
)fitincard
= 1;
465 b
= mmesa
->texHeap
[1];
469 if(b
->free
) if(t
->totalSize
<= b
->size
)fitinagp
= 1;
473 if(fitincard
)return 0;
474 if(fitinagp
)return 1;
476 if(totalcard
&& totalagp
)
479 int ratio
= (totalcard
> totalagp
) ? totalcard
/ totalagp
: totalagp
/ totalcard
;
480 ages
= mmesa
->sarea
->texAge
[0] + mmesa
->sarea
->texAge
[1];
481 if( (ages
% ratio
) == 0)return totalcard
> totalagp
? 1 : 0;
482 else return totalcard
> totalagp
? 0 : 1;
485 if(totalagp
) return 1;
490 int mgaUploadTexImages( mgaContextPtr mmesa
, mgaTextureObjectPtr t
)
496 heap
= t
->heap
= mgaChooseTexHeap( mmesa
, t
);
498 /* Do we need to eject LRU texture objects?
503 mgaTextureObjectPtr tmp
= mmesa
->TexObjList
[heap
].prev
;
505 t
->MemBlock
= mmAllocMem( mmesa
->texHeap
[heap
],
511 if (mmesa
->TexObjList
[heap
].prev
->bound
) {
512 fprintf(stderr
, "Hit bound texture in upload\n");
516 if (mmesa
->TexObjList
[heap
].prev
==
517 &(mmesa
->TexObjList
[heap
]))
519 fprintf(stderr
, "Failed to upload texture, sz %d\n", t
->totalSize
);
520 mmDumpMemInfo( mmesa
->texHeap
[heap
] );
524 mgaDestroyTexObj( mmesa
, tmp
);
527 ofs
= t
->MemBlock
->ofs
528 + mmesa
->mgaScreen
->textureOffset
[heap
]
531 t
->setup
.texorg
= ofs
;
532 t
->setup
.texorg1
= ofs
+ t
->offsets
[1];
533 t
->setup
.texorg2
= ofs
+ t
->offsets
[2];
534 t
->setup
.texorg3
= ofs
+ t
->offsets
[3];
535 t
->setup
.texorg4
= ofs
+ t
->offsets
[4];
537 mmesa
->dirty
|= MGA_UPLOAD_CONTEXT
;
540 /* Let the world know we've used this memory recently.
542 mgaUpdateTexLRU( mmesa
, t
);
545 if (MGA_DEBUG
&DEBUG_VERBOSE_LRU
)
546 fprintf(stderr
, "dispatch age: %d age freed memory: %d\n",
547 GET_DISPATCH_AGE(mmesa
), mmesa
->dirtyAge
);
549 if (mmesa
->dirtyAge
>= GET_DISPATCH_AGE(mmesa
))
550 mgaWaitAgeLocked( mmesa
, mmesa
->dirtyAge
);
552 if (t
->dirty_images
) {
553 if (MGA_DEBUG
&DEBUG_VERBOSE_LRU
)
554 fprintf(stderr
, "*");
556 for (i
= 0 ; i
<= t
->lastLevel
; i
++)
557 if (t
->dirty_images
& (1<<i
))
558 mgaUploadTexLevel( mmesa
, t
, i
);