mga driver, brought over by Jon Smirl
[mesa.git] / src / mesa / drivers / dri / mga / mgatexmem.c
1 /*
2 * Copyright 2000-2001 VA Linux Systems, Inc.
3 * All Rights Reserved.
4 *
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:
11 *
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
14 * Software.
15 *
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.
23 *
24 * Authors:
25 * Keith Whitwell <keith@tungstengraphics.com>
26 */
27 /* $XFree86: xc/lib/GL/mesa/src/drv/mga/mgatexmem.c,v 1.7 2002/10/30 12:51:36 alanh Exp $ */
28
29 #include <stdlib.h>
30 #include <stdio.h>
31 #include <GL/gl.h>
32
33 #include "mm.h"
34 #include "mgacontext.h"
35 #include "mgatex.h"
36 #include "mgaregs.h"
37 #include "mgaioctl.h"
38
39 /*#include "mem.h" */
40 #include "simple_list.h"
41
42 static void
43 mgaSwapOutTexObj(mgaContextPtr mmesa, mgaTextureObjectPtr t)
44 {
45 if (t->MemBlock) {
46 mmFreeMem(t->MemBlock);
47 t->MemBlock = 0;
48
49 if (t->age > mmesa->dirtyAge)
50 mmesa->dirtyAge = t->age;
51 }
52
53 t->dirty_images = ~0;
54 move_to_tail(&(mmesa->SwappedOut), t);
55 }
56
57 static void
58 mgaPrintLocalLRU( mgaContextPtr mmesa, int heap )
59 {
60 mgaTextureObjectPtr t;
61 int sz = 1 << (mmesa->mgaScreen->logTextureGranularity[heap]);
62
63 fprintf(stderr, "\nLocal LRU, heap %d:\n", heap);
64
65 foreach( t, &(mmesa->TexObjList[heap]) ) {
66 if (!t->tObj)
67 fprintf(stderr, "Placeholder %d at %x sz %x\n",
68 t->MemBlock->ofs / sz,
69 t->MemBlock->ofs,
70 t->MemBlock->size);
71 else
72 fprintf(stderr, "Texture (bound %d) at %x sz %x\n",
73 t->bound,
74 t->MemBlock->ofs,
75 t->MemBlock->size);
76 }
77
78 fprintf(stderr, "\n\n");
79 }
80
81 static void
82 mgaPrintGlobalLRU( mgaContextPtr mmesa, int heap )
83 {
84 int i, j;
85 drmTextureRegion *list = mmesa->sarea->texList[heap];
86
87 fprintf(stderr, "\nGlobal LRU, heap %d list %p:\n", heap, list);
88
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);
92 j = list[j].next;
93 if (j == MGA_NR_TEX_REGIONS) break;
94 }
95
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);
101 }
102 }
103
104 fprintf(stderr, "\n\n");
105 }
106
107
108 static void mgaResetGlobalLRU( mgaContextPtr mmesa, GLuint heap )
109 {
110 drmTextureRegion *list = mmesa->sarea->texList[heap];
111 int sz = 1 << mmesa->mgaScreen->logTextureGranularity[heap];
112 int i;
113
114 mmesa->texAge[heap] = ++mmesa->sarea->texAge[heap];
115
116 if (0) fprintf(stderr, "mgaResetGlobalLRU %d\n", (int)heap);
117
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
122 * memory.
123 */
124 for (i = 0 ; (i+1) * sz <= mmesa->mgaScreen->textureSize[heap] ; i++) {
125 list[i].prev = i-1;
126 list[i].next = i+1;
127 list[i].age = mmesa->sarea->texAge[heap];
128 }
129
130 i--;
131 list[0].prev = MGA_NR_TEX_REGIONS;
132 list[i].prev = i-1;
133 list[i].next = MGA_NR_TEX_REGIONS;
134 list[MGA_NR_TEX_REGIONS].prev = i;
135 list[MGA_NR_TEX_REGIONS].next = 0;
136
137 }
138
139
140 static void mgaUpdateTexLRU( mgaContextPtr mmesa, mgaTextureObjectPtr t )
141 {
142 int i;
143 int heap = t->heap;
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];
148
149 mmesa->texAge[heap] = ++mmesa->sarea->texAge[heap];
150
151 if (!t->MemBlock) {
152 fprintf(stderr, "no memblock\n\n");
153 return;
154 }
155
156 /* Update our local LRU
157 */
158 move_to_head( &(mmesa->TexObjList[heap]), t );
159
160
161 if (0)
162 fprintf(stderr, "mgaUpdateTexLRU heap %d list %p\n", heap, list);
163
164
165 /* Update the global LRU
166 */
167 for (i = start ; i <= end ; i++) {
168
169 list[i].in_use = 1;
170 list[i].age = mmesa->texAge[heap];
171
172 /* remove_from_list(i)
173 */
174 list[(unsigned)list[i].next].prev = list[i].prev;
175 list[(unsigned)list[i].prev].next = list[i].next;
176
177 /* insert_at_head(list, i)
178 */
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;
183 }
184
185 if (0) {
186 mgaPrintGlobalLRU(mmesa, t->heap);
187 mgaPrintLocalLRU(mmesa, t->heap);
188 }
189 }
190
191 /* Called for every shared texture region which has increased in age
192 * since we last held the lock.
193 *
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.
197 */
198 static void mgaTexturesGone( mgaContextPtr mmesa,
199 GLuint heap,
200 GLuint offset,
201 GLuint size,
202 GLuint in_use )
203 {
204 mgaTextureObjectPtr t, tmp;
205
206
207
208 foreach_s ( t, tmp, &(mmesa->TexObjList[heap]) ) {
209
210 if (t->MemBlock->ofs >= offset + size ||
211 t->MemBlock->ofs + t->MemBlock->size <= offset)
212 continue;
213
214
215
216
217 /* It overlaps - kick it off. Need to hold onto the currently bound
218 * objects, however.
219 */
220 if (t->bound)
221 mgaSwapOutTexObj( mmesa, t );
222 else
223 mgaDestroyTexObj( mmesa, t );
224 }
225
226
227 if (in_use) {
228 t = (mgaTextureObjectPtr) calloc(1, sizeof(*t));
229 if (!t) return;
230
231 t->heap = heap;
232 t->MemBlock = mmAllocMem( mmesa->texHeap[heap], size, 0, offset);
233 if (!t->MemBlock) {
234 fprintf(stderr, "Couldn't alloc placeholder sz %x ofs %x\n",
235 (int)size, (int)offset);
236 mmDumpMemInfo( mmesa->texHeap[heap]);
237 return;
238 }
239 insert_at_head( &(mmesa->TexObjList[heap]), t );
240 }
241 }
242
243
244 void mgaAgeTextures( mgaContextPtr mmesa, int heap )
245 {
246 MGASAREAPrivPtr sarea = mmesa->sarea;
247 int sz = 1 << (mmesa->mgaScreen->logTextureGranularity[heap]);
248 int idx, nr = 0;
249
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.
252 */
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++)
256 {
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.
260 */
261 if ( idx * sz > mmesa->mgaScreen->textureSize[heap] ) {
262 nr = MGA_NR_TEX_REGIONS;
263 break;
264 }
265
266 if (sarea->texList[heap][idx].age > mmesa->texAge[heap]) {
267 mgaTexturesGone(mmesa, heap, idx * sz, sz,
268 sarea->texList[heap][idx].in_use);
269 }
270 }
271
272 if (nr == MGA_NR_TEX_REGIONS) {
273 mgaTexturesGone(mmesa, heap, 0,
274 mmesa->mgaScreen->textureSize[heap], 0);
275 mgaResetGlobalLRU( mmesa, heap );
276 }
277
278
279 if (0) {
280 mgaPrintGlobalLRU( mmesa, heap );
281 mgaPrintLocalLRU( mmesa, heap );
282 }
283
284 mmesa->texAge[heap] = sarea->texAge[heap];
285 mmesa->dirty |= MGA_UPLOAD_TEX0IMAGE | MGA_UPLOAD_TEX1IMAGE;
286 }
287
288 /*
289 * mgaUploadSubImageLocked
290 *
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.
293 *
294 * Performed with the hardware lock held.
295 */
296 void mgaUploadSubImageLocked( mgaContextPtr mmesa,
297 mgaTextureObjectPtr t,
298 int level,
299 int x, int y, int width, int height )
300 {
301 int x2;
302 int dwords;
303 int offset;
304 struct gl_texture_image *image;
305 int texelBytes, texelsPerDword, texelMaccess, length;
306
307 if ( level < 0 || level >= MGA_TEX_MAXLEVELS )
308 return;
309
310 image = t->tObj->Image[level];
311 if ( !image ) return;
312
313
314 if (image->Data == 0) {
315 fprintf(stderr, "null texture image data tObj %p level %d\n",
316 t->tObj, level);
317 return;
318 }
319
320
321 /* find the proper destination offset for this level */
322 offset = (t->MemBlock->ofs +
323 t->offsets[level]);
324
325
326 texelBytes = t->texelBytes;
327 switch( texelBytes ) {
328 case 1:
329 texelsPerDword = 4;
330 texelMaccess = 0;
331 break;
332 case 2:
333 texelsPerDword = 2;
334 texelMaccess = 1;
335 break;
336 case 4:
337 texelsPerDword = 1;
338 texelMaccess = 2;
339 break;
340 default:
341 return;
342 }
343
344
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.
348 */
349 if ( image->Width < 32 ) {
350 x = 0;
351 width = image->Width * height;
352 height = 1;
353
354 /* Assume that 1x1 textures aren't going to cause a
355 * bus error if we read up to four texels from that
356 * location:
357 */
358 /* if ( width < texelsPerDword ) { */
359 /* width = texelsPerDword; */
360 /* } */
361 } else {
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 */
365 x2 = x + width;
366 x2 = (x2 + (texelsPerDword-1)) & ~(texelsPerDword-1);
367 x = (x + (texelsPerDword-1)) & ~(texelsPerDword-1);
368 width = x2 - x;
369 }
370
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. */
374 while ( 1 ) {
375 dwords = height * width / texelsPerDword;
376 if ( dwords * 4 <= MGA_BUFFER_SIZE ) {
377 break;
378 }
379
380 mgaUploadSubImageLocked( mmesa, t, level, x, y,
381 width, height >> 1 );
382 y += ( height >> 1 );
383 height -= ( height >> 1 );
384 }
385
386 length = dwords * 4;
387
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;
396
397 if (0)
398 fprintf(stderr, "TexelBytes : %d, offset: %d, length : %d\n",
399 texelBytes,
400 mmesa->mgaScreen->textureOffset[t->heap] +
401 offset +
402 y * width * 4/texelsPerDword,
403 length);
404
405 mgaFireILoadLocked( mmesa,
406 mmesa->mgaScreen->textureOffset[t->heap] +
407 offset +
408 y * width * 4/texelsPerDword,
409 length);
410 } else {
411 /* This works, is slower for uploads to card space and needs
412 * additional synchronization with the dma stream.
413 */
414
415 UPDATE_LOCK(mmesa, DRM_LOCK_FLUSH | DRM_LOCK_QUIESCENT);
416 mgaConvertTexture( (GLuint *)
417 (mmesa->mgaScreen->texVirtual[t->heap] +
418 offset +
419 y * width * 4/texelsPerDword),
420 texelBytes, image, x, y, width, height );
421 }
422 }
423
424
425 static void mgaUploadTexLevel( mgaContextPtr mmesa,
426 mgaTextureObjectPtr t,
427 int l )
428 {
429 mgaUploadSubImageLocked( mmesa,
430 t,
431 l,
432 0, 0,
433 t->tObj->Image[l]->Width,
434 t->tObj->Image[l]->Height);
435 }
436
437
438
439
440 #if 0
441 static void mgaMigrateTexture( mgaContextPtr mmesa, mgaTextureObjectPtr t )
442 {
443 /* NOT DONE */
444 }
445 #endif
446
447
448 static int mgaChooseTexHeap( mgaContextPtr mmesa, mgaTextureObjectPtr t )
449 {
450 int freeagp, freecard;
451 int fitincard, fitinagp;
452 int totalcard, totalagp;
453 TMemBlock *b;
454
455 totalcard = totalagp = fitincard = fitinagp = freeagp = freecard = 0;
456
457 b = mmesa->texHeap[0];
458 while(b)
459 {
460 totalcard += b->size;
461 if(b->free) if(t->totalSize <= b->size)fitincard = 1;
462 b = b->next;
463 }
464
465 b = mmesa->texHeap[1];
466 while(b)
467 {
468 totalagp += b->size;
469 if(b->free) if(t->totalSize <= b->size)fitinagp = 1;
470 b = b->next;
471 }
472
473 if(fitincard)return 0;
474 if(fitinagp)return 1;
475
476 if(totalcard && totalagp)
477 {
478 int ages;
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;
483 }
484
485 if(totalagp) return 1;
486 return 0;
487 }
488
489
490 int mgaUploadTexImages( mgaContextPtr mmesa, mgaTextureObjectPtr t )
491 {
492 int heap;
493 int i;
494 int ofs;
495
496 heap = t->heap = mgaChooseTexHeap( mmesa, t );
497
498 /* Do we need to eject LRU texture objects?
499 */
500 if (!t->MemBlock) {
501 while (1)
502 {
503 mgaTextureObjectPtr tmp = mmesa->TexObjList[heap].prev;
504
505 t->MemBlock = mmAllocMem( mmesa->texHeap[heap],
506 t->totalSize,
507 6, 0 );
508 if (t->MemBlock)
509 break;
510
511 if (mmesa->TexObjList[heap].prev->bound) {
512 fprintf(stderr, "Hit bound texture in upload\n");
513 return -1;
514 }
515
516 if (mmesa->TexObjList[heap].prev ==
517 &(mmesa->TexObjList[heap]))
518 {
519 fprintf(stderr, "Failed to upload texture, sz %d\n", t->totalSize);
520 mmDumpMemInfo( mmesa->texHeap[heap] );
521 return -1;
522 }
523
524 mgaDestroyTexObj( mmesa, tmp );
525 }
526
527 ofs = t->MemBlock->ofs
528 + mmesa->mgaScreen->textureOffset[heap]
529 ;
530
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];
536
537 mmesa->dirty |= MGA_UPLOAD_CONTEXT;
538 }
539
540 /* Let the world know we've used this memory recently.
541 */
542 mgaUpdateTexLRU( mmesa, t );
543
544
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);
548
549 if (mmesa->dirtyAge >= GET_DISPATCH_AGE(mmesa))
550 mgaWaitAgeLocked( mmesa, mmesa->dirtyAge );
551
552 if (t->dirty_images) {
553 if (MGA_DEBUG&DEBUG_VERBOSE_LRU)
554 fprintf(stderr, "*");
555
556 for (i = 0 ; i <= t->lastLevel ; i++)
557 if (t->dirty_images & (1<<i))
558 mgaUploadTexLevel( mmesa, t, i );
559 }
560
561
562 t->dirty_images = 0;
563 return 0;
564 }