Tizen 2.0 Release
[profile/ivi/osmesa.git] / src / mesa / drivers / dri / mach64 / mach64_texmem.c
1 /* -*- mode: c; c-basic-offset: 3 -*- */
2 /*
3  * Copyright 1999, 2000 ATI Technologies Inc. and Precision Insight, Inc.,
4  *                                                Cedar Park, Texas.
5  * All Rights Reserved.
6  *
7  * Permission is hereby granted, free of charge, to any person obtaining a
8  * copy of this software and associated documentation files (the "Software"),
9  * to deal in the Software without restriction, including without limitation
10  * on the rights to use, copy, modify, merge, publish, distribute, sub
11  * license, and/or sell copies of the Software, and to permit persons to whom
12  * the Software is furnished to do so, subject to the following conditions:
13  *
14  * The above copyright notice and this permission notice (including the next
15  * paragraph) shall be included in all copies or substantial portions of the
16  * Software.
17  *
18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20  * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
21  * ATI, PRECISION INSIGHT AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
22  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
23  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
24  * USE OR OTHER DEALINGS IN THE SOFTWARE.
25  */
26
27 /*
28  * Authors:
29  *   Gareth Hughes <gareth@valinux.com>
30  *   Leif Delgass <ldelgass@retinalburn.net>
31  *   Jose Fonseca <j_r_fonseca@yahoo.co.uk>
32  */
33
34 #include "main/context.h"
35 #include "main/macros.h"
36 #include "main/simple_list.h"
37 #include "main/imports.h"
38
39 #include "mach64_context.h"
40 #include "mach64_ioctl.h"
41 #include "mach64_tex.h"
42
43
44 /* Destroy hardware state associated with texture `t'.
45  */
46 void mach64DestroyTexObj( mach64ContextPtr mmesa, mach64TexObjPtr t )
47 {
48    unsigned   i;
49
50    /* See if it was the driver's current object.
51     */
52    if ( mmesa != NULL )
53    {
54       for ( i = 0 ; i < mmesa->glCtx->Const.MaxTextureUnits ; i++ )
55       {
56          if ( t == mmesa->CurrentTexObj[ i ] ) {
57             assert( t->base.bound & (1 << i) );
58             mmesa->CurrentTexObj[ i ] = NULL;
59          }
60       }
61    }
62 }
63
64 /* Upload the texture image associated with texture `t' at level `level'
65  * at the address relative to `start'.
66  */
67 static void mach64UploadAGPSubImage( mach64ContextPtr mmesa,
68                                      mach64TexObjPtr t, int level,
69                                      int x, int y, int width, int height )
70 {
71    mach64ScreenRec *mach64Screen = mmesa->mach64Screen;
72    struct gl_texture_image *image;
73    int texelsPerDword = 0;
74    int dwords;
75    GLuint texelBytes;
76
77    /* Ensure we have a valid texture to upload */
78    if ( ( level < 0 ) || ( level > mmesa->glCtx->Const.MaxTextureLevels ) )
79      return;
80
81    image = t->base.tObj->Image[0][level];
82    if ( !image )
83       return;
84
85    texelBytes = _mesa_get_format_bytes(image->TexFormat);
86
87    switch ( texelBytes ) {
88    case 1: texelsPerDword = 4; break;
89    case 2: texelsPerDword = 2; break;
90    case 4: texelsPerDword = 1; break;
91    }
92
93 #if 1
94    /* FIXME: The subimage index calcs are wrong... */
95    x = 0;
96    y = 0;
97    width = image->Width;
98    height = image->Height;
99 #endif
100
101    dwords = width * height / texelsPerDword;
102
103 #if ENABLE_PERF_BOXES
104    /* Bump the performance counter */
105    mmesa->c_agpTextureBytes += (dwords << 2);
106 #endif
107
108    if ( MACH64_DEBUG & DEBUG_VERBOSE_API ) {
109       fprintf( stderr, "mach64UploadSubImage: %d,%d of %d,%d at %d,%d\n",
110                width, height, image->Width, image->Height, x, y );
111       fprintf( stderr, "            blit ofs: 0x%07x pitch: 0x%x dwords: %d\n",
112                (GLuint)t->bufAddr, (GLint)width, dwords );
113    }
114
115    assert(image->Data);
116
117    {
118       CARD32 *dst = (CARD32 *)((char *)mach64Screen->agpTextures.map + t->base.memBlock->ofs);
119       const GLubyte *src = (const GLubyte *) image->Data +
120          (y * image->Width + x) * texelBytes;
121       const GLuint bytes = width * height * texelBytes;
122       memcpy(dst, src, bytes);
123    }
124
125 }
126
127 /* Upload the texture image associated with texture `t' at level `level'
128  * at the address relative to `start'.
129  */
130 static void mach64UploadLocalSubImage( mach64ContextPtr mmesa,
131                                   mach64TexObjPtr t, int level,
132                                   int x, int y, int width, int height )
133 {
134    struct gl_texture_image *image;
135    int texelsPerDword = 0;
136    int imageWidth, imageHeight;
137    int remaining, rows;
138    int format, dwords;
139    const int maxdwords = (MACH64_BUFFER_MAX_DWORDS - (MACH64_HOSTDATA_BLIT_OFFSET / 4));
140    CARD32 pitch, offset;
141    int i;
142    GLuint texelBytes;
143
144    /* Ensure we have a valid texture to upload */
145    if ( ( level < 0 ) || ( level > mmesa->glCtx->Const.MaxTextureLevels ) )
146       return;
147
148    image = t->base.tObj->Image[0][level];
149    if ( !image )
150       return;
151
152    texelBytes = _mesa_get_format_bytes(image->TexFormat);
153
154    switch ( texelBytes ) {
155    case 1: texelsPerDword = 4; break;
156    case 2: texelsPerDword = 2; break;
157    case 4: texelsPerDword = 1; break;
158    }
159
160 #if 1
161    /* FIXME: The subimage index calcs are wrong... */
162    x = 0;
163    y = 0;
164    width = image->Width;
165    height = image->Height;
166 #endif
167
168    imageWidth  = image->Width;
169    imageHeight = image->Height;
170
171    format = t->textureFormat;
172
173    /* The texel upload routines have a minimum width, so force the size
174     * if needed.
175     */
176    if ( imageWidth < texelsPerDword ) {
177       int factor;
178
179       factor = texelsPerDword / imageWidth;
180       imageWidth = texelsPerDword;
181       imageHeight /= factor;
182       if ( imageHeight == 0 ) {
183          /* In this case, the texel converter will actually walk a
184           * texel or two off the end of the image, but normal malloc
185           * alignment should prevent it from ever causing a fault.
186           */
187          imageHeight = 1;
188       }
189    }
190
191    /* We can't upload to a pitch less than 64 texels so we will need to
192     * linearly upload all modified rows for textures smaller than this.
193     * This makes the x/y/width/height different for the blitter and the
194     * texture walker.
195     */
196    if ( imageWidth >= 64 ) {
197       /* The texture walker and the blitter look identical */
198       pitch = imageWidth >> 3;
199    } else {
200       int factor;
201       int y2;
202       int start, end;
203
204       start = (y * imageWidth) & ~63;
205       end = (y + height) * imageWidth;
206
207       if ( end - start < 64 ) {
208          /* Handle the case where the total number of texels
209           * uploaded is < 64.
210           */
211          x = 0;
212          y = start / 64;
213          width = end - start;
214          height = 1;
215       } else {
216          /* Upload some number of full 64 texel blit rows */
217          factor = 64 / imageWidth;
218
219          y2 = y + height - 1;
220          y /= factor;
221          y2 /= factor;
222
223          x = 0;
224          width = 64;
225          height = y2 - y + 1;
226       }
227
228       /* Fixed pitch of 64 */
229       pitch = 8;
230    }
231
232    dwords = width * height / texelsPerDword;
233    offset = t->bufAddr;
234
235 #if ENABLE_PERF_BOXES
236    /* Bump the performance counter */
237    mmesa->c_textureBytes += (dwords << 2);
238 #endif
239
240    if ( MACH64_DEBUG & DEBUG_VERBOSE_API ) {
241       fprintf( stderr, "mach64UploadSubImage: %d,%d of %d,%d at %d,%d\n",
242                width, height, image->Width, image->Height, x, y );
243       fprintf( stderr, "            blit ofs: 0x%07x pitch: 0x%x dwords: %d\n",
244                (GLuint)offset, (GLint)width, dwords );
245    }
246
247    /* Subdivide the texture if required (account for the registers added by the drm) */
248    if ( dwords <= maxdwords ) {
249       rows = height;
250    } else {
251       rows = (maxdwords * texelsPerDword) / (2 * width);
252    }
253
254    for ( i = 0, remaining = height ;
255          remaining > 0 ;
256          remaining -= rows, y += rows, i++ )
257    {
258        height = MIN2(remaining, rows);
259
260        assert(image->Data);
261
262        {
263           const GLubyte *src = (const GLubyte *) image->Data +
264              (y * image->Width + x) * texelBytes;
265
266           mach64FireBlitLocked( mmesa, (void *)src, offset, pitch, format,
267                                 x, y, width, height );
268        }
269
270    }
271
272    mmesa->new_state |= MACH64_NEW_CONTEXT;
273    mmesa->dirty |= MACH64_UPLOAD_CONTEXT | MACH64_UPLOAD_MISC;
274 }
275
276
277 /* Upload the texture images associated with texture `t'.  This might
278  * require removing our own and/or other client's texture objects to
279  * make room for these images.
280  */
281 void mach64UploadTexImages( mach64ContextPtr mmesa, mach64TexObjPtr t )
282 {
283    if ( MACH64_DEBUG & DEBUG_VERBOSE_API ) {
284       fprintf( stderr, "%s( %p, %p )\n",
285                __FUNCTION__, mmesa->glCtx, t );
286    }
287
288    assert(t);
289    assert(t->base.tObj);
290
291    if ( !t->base.memBlock ) {
292       int heap;
293
294       /* NULL heaps are skipped */
295       heap = driAllocateTexture( mmesa->texture_heaps, MACH64_NR_TEX_HEAPS,
296                                  (driTextureObject *) t );
297
298       if ( heap == -1 ) {
299          fprintf( stderr, "%s: upload texture failure, sz=%d\n", __FUNCTION__,
300                   t->base.totalSize );
301          exit(-1);
302          return;
303       }
304
305       t->heap = heap;
306
307       /* Set the base offset of the texture image */
308       assert(t->base.memBlock);
309       t->bufAddr = mmesa->mach64Screen->texOffset[heap] + t->base.memBlock->ofs;
310
311       /* Force loading the new state into the hardware */
312       mmesa->dirty |= (MACH64_UPLOAD_SCALE_3D_CNTL |
313                        MACH64_UPLOAD_TEXTURE);
314    }
315
316    /* Let the world know we've used this memory recently */
317    driUpdateTextureLRU( (driTextureObject *) t );
318
319    /* Upload any images that are new */
320    if ( t->base.dirty_images[0] ) {
321       const GLint j = t->base.tObj->BaseLevel;
322       if (t->heap == MACH64_AGP_HEAP) {
323          /* Need to make sure any vertex buffers in the queue complete */
324          mach64WaitForIdleLocked( mmesa );
325          mach64UploadAGPSubImage( mmesa, t, j, 0, 0,
326                                   t->base.tObj->Image[0][j]->Width,
327                                   t->base.tObj->Image[0][j]->Height );
328       } else {
329          mach64UploadLocalSubImage( mmesa, t, j, 0, 0,
330                                     t->base.tObj->Image[0][j]->Width,
331                                     t->base.tObj->Image[0][j]->Height );
332       }
333
334       mmesa->setup.tex_cntl |= MACH64_TEX_CACHE_FLUSH;
335       t->base.dirty_images[0] = 0;
336    }
337
338    mmesa->dirty |= MACH64_UPLOAD_TEXTURE;
339 }
340
341
342 /* Allocate memory from the same texture heap `heap' for both textures
343  * `u0' and `u1'.
344  */
345 static int mach64AllocateMultiTex( mach64ContextPtr mmesa,
346                                    mach64TexObjPtr u0,
347                                    mach64TexObjPtr u1,
348                                    int heap, GLboolean alloc_u0 )
349 {
350    /* Both objects should be bound */
351    assert( u0->base.bound && u1->base.bound );
352
353    if ( alloc_u0 ) {
354       /* Evict u0 from its current heap */
355       if ( u0->base.memBlock ) {
356          assert( u0->heap != heap );
357          driSwapOutTextureObject( (driTextureObject *) u0 );
358       }
359
360       /* Try to allocate u0 in the chosen heap */
361       u0->heap = driAllocateTexture( &mmesa->texture_heaps[heap], 1,
362                                      (driTextureObject *) u0 );
363
364       if ( u0->heap == -1 ) {
365          return -1;
366       }
367    }
368
369    /* Evict u1 from its current heap */
370    if ( u1->base.memBlock ) {
371       assert( u1->heap != heap );
372       driSwapOutTextureObject( (driTextureObject *) u1 );
373    }
374
375    /* Try to allocate u1 in the same heap as u0 */
376    u1->heap = driAllocateTexture( &mmesa->texture_heaps[heap], 1,
377                                   (driTextureObject *) u1 );
378
379    if ( u1->heap == -1 ) {
380       return -1;
381    }
382
383    /* Bound objects are not evicted */
384    assert( u0->base.memBlock && u1->base.memBlock );
385    assert( u0->heap == u1->heap );
386
387    return heap;
388 }
389
390 /* The mach64 needs to have both primary and secondary textures in either
391  * local or AGP memory, so we need a "buddy system" to make sure that allocation
392  * succeeds or fails for both textures.
393  */
394 void mach64UploadMultiTexImages( mach64ContextPtr mmesa, 
395                                  mach64TexObjPtr t0,
396                                  mach64TexObjPtr t1 )
397 {
398    if ( MACH64_DEBUG & DEBUG_VERBOSE_API ) {
399       fprintf( stderr, "%s( %p, %p %p )\n",
400                __FUNCTION__, mmesa->glCtx, t0, t1 );
401    }
402
403    assert(t0 && t1);
404    assert(t0->base.tObj && t1->base.tObj);
405
406    if ( !t0->base.memBlock || !t1->base.memBlock || t0->heap != t1->heap ) {
407       mach64TexObjPtr u0 = NULL;
408       mach64TexObjPtr u1 = NULL;
409       unsigned totalSize = t0->base.totalSize + t1->base.totalSize;
410
411       int heap, ret;
412
413       /* Check if one of the textures is already swapped in a heap and the
414        * other texture fits in that heap.
415        */
416       if ( t0->base.memBlock && totalSize <= t0->base.heap->size ) {
417          u0 = t0;
418          u1 = t1;
419       } else if ( t1->base.memBlock && totalSize <= t1->base.heap->size ) {
420          u0 = t1;
421          u1 = t0;
422       }
423
424       if ( u0 ) {
425          heap = u0->heap;
426
427          ret = mach64AllocateMultiTex( mmesa, u0, u1, heap, GL_FALSE );
428       } else {
429          /* Both textures are swapped out or collocation is impossible */
430          u0 = t0;
431          u1 = t1;
432
433          /* Choose the heap appropriately */
434          heap = MACH64_CARD_HEAP;
435
436          if ( totalSize > mmesa->texture_heaps[heap]->size ) {
437             heap = MACH64_AGP_HEAP;
438          }
439
440          ret = mach64AllocateMultiTex( mmesa, u0, u1, heap, GL_TRUE );
441       }
442
443       if ( ret == -1 && heap == MACH64_CARD_HEAP ) {
444          /* Try AGP if local memory failed */
445          heap = MACH64_AGP_HEAP;
446
447          ret = mach64AllocateMultiTex( mmesa, u0, u1, heap, GL_TRUE );
448       }
449
450       if ( ret == -1 ) {
451          /* FIXME:
452           * Swap out all textures from the AGP heap and re-run allocation, this
453           * should succeed in all cases.
454           */
455          fprintf( stderr, "%s: upload multi-texture failure, sz0=%d sz1=%d\n",
456                   __FUNCTION__, t0->base.totalSize, t1->base.totalSize );
457          exit(-1);
458       }
459
460       /* Set the base offset of the texture image */
461       assert(t0->base.memBlock);
462       t0->bufAddr = mmesa->mach64Screen->texOffset[heap] + t0->base.memBlock->ofs;
463       assert(t1->base.memBlock);
464       t1->bufAddr = mmesa->mach64Screen->texOffset[heap] + t1->base.memBlock->ofs;
465
466       /* Force loading the new state into the hardware */
467       mmesa->dirty |= (MACH64_UPLOAD_SCALE_3D_CNTL |
468                        MACH64_UPLOAD_TEXTURE);
469    }
470
471    /* Let the world know we've used this memory recently */
472    driUpdateTextureLRU( (driTextureObject *) t0 );
473    driUpdateTextureLRU( (driTextureObject *) t1 );
474
475    /* Upload any images that are new */
476    if ( t0->base.dirty_images[0] ) {
477       const GLint j0 = t0->base.tObj->BaseLevel;
478       if (t0->heap == MACH64_AGP_HEAP) {
479          /* Need to make sure any vertex buffers in the queue complete */
480          mach64WaitForIdleLocked( mmesa );
481          mach64UploadAGPSubImage( mmesa, t0, j0, 0, 0,
482                                     t0->base.tObj->Image[0][j0]->Width,
483                                     t0->base.tObj->Image[0][j0]->Height );
484       } else {
485          mach64UploadLocalSubImage( mmesa, t0, j0, 0, 0,
486                                     t0->base.tObj->Image[0][j0]->Width,
487                                     t0->base.tObj->Image[0][j0]->Height );
488       }
489       mmesa->setup.tex_cntl |= MACH64_TEX_CACHE_FLUSH;
490       t0->base.dirty_images[0] = 0;
491    }
492    if ( t1->base.dirty_images[0] ) {
493       const GLint j1 = t1->base.tObj->BaseLevel;
494       if (t1->heap == MACH64_AGP_HEAP) {
495          /* Need to make sure any vertex buffers in the queue complete */
496          mach64WaitForIdleLocked( mmesa );
497          mach64UploadAGPSubImage( mmesa, t1, j1, 0, 0,
498                                t1->base.tObj->Image[0][j1]->Width,
499                                t1->base.tObj->Image[0][j1]->Height );
500       } else {
501          mach64UploadLocalSubImage( mmesa, t1, j1, 0, 0,
502                                t1->base.tObj->Image[0][j1]->Width,
503                                t1->base.tObj->Image[0][j1]->Height );
504       }
505       
506       mmesa->setup.tex_cntl |= MACH64_TEX_CACHE_FLUSH;
507       t1->base.dirty_images[0] = 0;
508    }
509
510    mmesa->dirty |= MACH64_UPLOAD_TEXTURE;
511 }