Merge branch 'mesa_7_6_branch'
[mesa.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_state.h"
41 #include "mach64_ioctl.h"
42 #include "mach64_vb.h"
43 #include "mach64_tris.h"
44 #include "mach64_tex.h"
45
46
47 /* Destroy hardware state associated with texture `t'.
48 */
49 void mach64DestroyTexObj( mach64ContextPtr mmesa, mach64TexObjPtr t )
50 {
51 unsigned i;
52
53 /* See if it was the driver's current object.
54 */
55 if ( mmesa != NULL )
56 {
57 for ( i = 0 ; i < mmesa->glCtx->Const.MaxTextureUnits ; i++ )
58 {
59 if ( t == mmesa->CurrentTexObj[ i ] ) {
60 assert( t->base.bound & (1 << i) );
61 mmesa->CurrentTexObj[ i ] = NULL;
62 }
63 }
64 }
65 }
66
67 /* Upload the texture image associated with texture `t' at level `level'
68 * at the address relative to `start'.
69 */
70 static void mach64UploadAGPSubImage( mach64ContextPtr mmesa,
71 mach64TexObjPtr t, int level,
72 int x, int y, int width, int height )
73 {
74 mach64ScreenRec *mach64Screen = mmesa->mach64Screen;
75 struct gl_texture_image *image;
76 int texelsPerDword = 0;
77 int dwords;
78 GLuint texelBytes;
79
80 /* Ensure we have a valid texture to upload */
81 if ( ( level < 0 ) || ( level > mmesa->glCtx->Const.MaxTextureLevels ) )
82 return;
83
84 image = t->base.tObj->Image[0][level];
85 if ( !image )
86 return;
87
88 texelBytes = _mesa_get_format_bytes(image->TexFormat);
89
90 switch ( texelBytes ) {
91 case 1: texelsPerDword = 4; break;
92 case 2: texelsPerDword = 2; break;
93 case 4: texelsPerDword = 1; break;
94 }
95
96 #if 1
97 /* FIXME: The subimage index calcs are wrong... */
98 x = 0;
99 y = 0;
100 width = image->Width;
101 height = image->Height;
102 #endif
103
104 dwords = width * height / texelsPerDword;
105
106 #if ENABLE_PERF_BOXES
107 /* Bump the performance counter */
108 mmesa->c_agpTextureBytes += (dwords << 2);
109 #endif
110
111 if ( MACH64_DEBUG & DEBUG_VERBOSE_API ) {
112 fprintf( stderr, "mach64UploadSubImage: %d,%d of %d,%d at %d,%d\n",
113 width, height, image->Width, image->Height, x, y );
114 fprintf( stderr, " blit ofs: 0x%07x pitch: 0x%x dwords: %d\n",
115 (GLuint)t->bufAddr, (GLint)width, dwords );
116 }
117
118 assert(image->Data);
119
120 {
121 CARD32 *dst = (CARD32 *)((char *)mach64Screen->agpTextures.map + t->base.memBlock->ofs);
122 const GLubyte *src = (const GLubyte *) image->Data +
123 (y * image->Width + x) * texelBytes;
124 const GLuint bytes = width * height * texelBytes;
125 memcpy(dst, src, bytes);
126 }
127
128 }
129
130 /* Upload the texture image associated with texture `t' at level `level'
131 * at the address relative to `start'.
132 */
133 static void mach64UploadLocalSubImage( mach64ContextPtr mmesa,
134 mach64TexObjPtr t, int level,
135 int x, int y, int width, int height )
136 {
137 struct gl_texture_image *image;
138 int texelsPerDword = 0;
139 int imageWidth, imageHeight;
140 int remaining, rows;
141 int format, dwords;
142 const int maxdwords = (MACH64_BUFFER_MAX_DWORDS - (MACH64_HOSTDATA_BLIT_OFFSET / 4));
143 CARD32 pitch, offset;
144 int i;
145 GLuint texelBytes;
146
147 /* Ensure we have a valid texture to upload */
148 if ( ( level < 0 ) || ( level > mmesa->glCtx->Const.MaxTextureLevels ) )
149 return;
150
151 image = t->base.tObj->Image[0][level];
152 if ( !image )
153 return;
154
155 texelBytes = _mesa_get_format_bytes(image->TexFormat);
156
157 switch ( texelBytes ) {
158 case 1: texelsPerDword = 4; break;
159 case 2: texelsPerDword = 2; break;
160 case 4: texelsPerDword = 1; break;
161 }
162
163 #if 1
164 /* FIXME: The subimage index calcs are wrong... */
165 x = 0;
166 y = 0;
167 width = image->Width;
168 height = image->Height;
169 #endif
170
171 imageWidth = image->Width;
172 imageHeight = image->Height;
173
174 format = t->textureFormat;
175
176 /* The texel upload routines have a minimum width, so force the size
177 * if needed.
178 */
179 if ( imageWidth < texelsPerDword ) {
180 int factor;
181
182 factor = texelsPerDword / imageWidth;
183 imageWidth = texelsPerDword;
184 imageHeight /= factor;
185 if ( imageHeight == 0 ) {
186 /* In this case, the texel converter will actually walk a
187 * texel or two off the end of the image, but normal malloc
188 * alignment should prevent it from ever causing a fault.
189 */
190 imageHeight = 1;
191 }
192 }
193
194 /* We can't upload to a pitch less than 64 texels so we will need to
195 * linearly upload all modified rows for textures smaller than this.
196 * This makes the x/y/width/height different for the blitter and the
197 * texture walker.
198 */
199 if ( imageWidth >= 64 ) {
200 /* The texture walker and the blitter look identical */
201 pitch = imageWidth >> 3;
202 } else {
203 int factor;
204 int y2;
205 int start, end;
206
207 start = (y * imageWidth) & ~63;
208 end = (y + height) * imageWidth;
209
210 if ( end - start < 64 ) {
211 /* Handle the case where the total number of texels
212 * uploaded is < 64.
213 */
214 x = 0;
215 y = start / 64;
216 width = end - start;
217 height = 1;
218 } else {
219 /* Upload some number of full 64 texel blit rows */
220 factor = 64 / imageWidth;
221
222 y2 = y + height - 1;
223 y /= factor;
224 y2 /= factor;
225
226 x = 0;
227 width = 64;
228 height = y2 - y + 1;
229 }
230
231 /* Fixed pitch of 64 */
232 pitch = 8;
233 }
234
235 dwords = width * height / texelsPerDword;
236 offset = t->bufAddr;
237
238 #if ENABLE_PERF_BOXES
239 /* Bump the performance counter */
240 mmesa->c_textureBytes += (dwords << 2);
241 #endif
242
243 if ( MACH64_DEBUG & DEBUG_VERBOSE_API ) {
244 fprintf( stderr, "mach64UploadSubImage: %d,%d of %d,%d at %d,%d\n",
245 width, height, image->Width, image->Height, x, y );
246 fprintf( stderr, " blit ofs: 0x%07x pitch: 0x%x dwords: %d\n",
247 (GLuint)offset, (GLint)width, dwords );
248 }
249
250 /* Subdivide the texture if required (account for the registers added by the drm) */
251 if ( dwords <= maxdwords ) {
252 rows = height;
253 } else {
254 rows = (maxdwords * texelsPerDword) / (2 * width);
255 }
256
257 for ( i = 0, remaining = height ;
258 remaining > 0 ;
259 remaining -= rows, y += rows, i++ )
260 {
261 height = MIN2(remaining, rows);
262
263 assert(image->Data);
264
265 {
266 const GLubyte *src = (const GLubyte *) image->Data +
267 (y * image->Width + x) * texelBytes;
268
269 mach64FireBlitLocked( mmesa, (void *)src, offset, pitch, format,
270 x, y, width, height );
271 }
272
273 }
274
275 mmesa->new_state |= MACH64_NEW_CONTEXT;
276 mmesa->dirty |= MACH64_UPLOAD_CONTEXT | MACH64_UPLOAD_MISC;
277 }
278
279
280 /* Upload the texture images associated with texture `t'. This might
281 * require removing our own and/or other client's texture objects to
282 * make room for these images.
283 */
284 void mach64UploadTexImages( mach64ContextPtr mmesa, mach64TexObjPtr t )
285 {
286 if ( MACH64_DEBUG & DEBUG_VERBOSE_API ) {
287 fprintf( stderr, "%s( %p, %p )\n",
288 __FUNCTION__, mmesa->glCtx, t );
289 }
290
291 assert(t);
292 assert(t->base.tObj);
293
294 if ( !t->base.memBlock ) {
295 int heap;
296
297 /* NULL heaps are skipped */
298 heap = driAllocateTexture( mmesa->texture_heaps, MACH64_NR_TEX_HEAPS,
299 (driTextureObject *) t );
300
301 if ( heap == -1 ) {
302 fprintf( stderr, "%s: upload texture failure, sz=%d\n", __FUNCTION__,
303 t->base.totalSize );
304 exit(-1);
305 return;
306 }
307
308 t->heap = heap;
309
310 /* Set the base offset of the texture image */
311 t->bufAddr = mmesa->mach64Screen->texOffset[heap] + t->base.memBlock->ofs;
312
313 /* Force loading the new state into the hardware */
314 mmesa->dirty |= (MACH64_UPLOAD_SCALE_3D_CNTL |
315 MACH64_UPLOAD_TEXTURE);
316 }
317
318 /* Let the world know we've used this memory recently */
319 driUpdateTextureLRU( (driTextureObject *) t );
320
321 /* Upload any images that are new */
322 if ( t->base.dirty_images[0] ) {
323 const GLint j = t->base.tObj->BaseLevel;
324 if (t->heap == MACH64_AGP_HEAP) {
325 /* Need to make sure any vertex buffers in the queue complete */
326 mach64WaitForIdleLocked( mmesa );
327 mach64UploadAGPSubImage( mmesa, t, j, 0, 0,
328 t->base.tObj->Image[0][j]->Width,
329 t->base.tObj->Image[0][j]->Height );
330 } else {
331 mach64UploadLocalSubImage( mmesa, t, j, 0, 0,
332 t->base.tObj->Image[0][j]->Width,
333 t->base.tObj->Image[0][j]->Height );
334 }
335
336 mmesa->setup.tex_cntl |= MACH64_TEX_CACHE_FLUSH;
337 t->base.dirty_images[0] = 0;
338 }
339
340 mmesa->dirty |= MACH64_UPLOAD_TEXTURE;
341 }
342
343
344 /* Allocate memory from the same texture heap `heap' for both textures
345 * `u0' and `u1'.
346 */
347 static int mach64AllocateMultiTex( mach64ContextPtr mmesa,
348 mach64TexObjPtr u0,
349 mach64TexObjPtr u1,
350 int heap, GLboolean alloc_u0 )
351 {
352 /* Both objects should be bound */
353 assert( u0->base.bound && u1->base.bound );
354
355 if ( alloc_u0 ) {
356 /* Evict u0 from its current heap */
357 if ( u0->base.memBlock ) {
358 assert( u0->heap != heap );
359 driSwapOutTextureObject( (driTextureObject *) u0 );
360 }
361
362 /* Try to allocate u0 in the chosen heap */
363 u0->heap = driAllocateTexture( &mmesa->texture_heaps[heap], 1,
364 (driTextureObject *) u0 );
365
366 if ( u0->heap == -1 ) {
367 return -1;
368 }
369 }
370
371 /* Evict u1 from its current heap */
372 if ( u1->base.memBlock ) {
373 assert( u1->heap != heap );
374 driSwapOutTextureObject( (driTextureObject *) u1 );
375 }
376
377 /* Try to allocate u1 in the same heap as u0 */
378 u1->heap = driAllocateTexture( &mmesa->texture_heaps[heap], 1,
379 (driTextureObject *) u1 );
380
381 if ( u1->heap == -1 ) {
382 return -1;
383 }
384
385 /* Bound objects are not evicted */
386 assert( u0->base.memBlock && u1->base.memBlock );
387 assert( u0->heap == u1->heap );
388
389 return heap;
390 }
391
392 /* The mach64 needs to have both primary and secondary textures in either
393 * local or AGP memory, so we need a "buddy system" to make sure that allocation
394 * succeeds or fails for both textures.
395 */
396 void mach64UploadMultiTexImages( mach64ContextPtr mmesa,
397 mach64TexObjPtr t0,
398 mach64TexObjPtr t1 )
399 {
400 if ( MACH64_DEBUG & DEBUG_VERBOSE_API ) {
401 fprintf( stderr, "%s( %p, %p %p )\n",
402 __FUNCTION__, mmesa->glCtx, t0, t1 );
403 }
404
405 assert(t0 && t1);
406 assert(t0->base.tObj && t1->base.tObj);
407
408 if ( !t0->base.memBlock || !t1->base.memBlock || t0->heap != t1->heap ) {
409 mach64TexObjPtr u0 = NULL;
410 mach64TexObjPtr u1 = NULL;
411 unsigned totalSize = t0->base.totalSize + t1->base.totalSize;
412
413 int heap, ret;
414
415 /* Check if one of the textures is already swapped in a heap and the
416 * other texture fits in that heap.
417 */
418 if ( t0->base.memBlock && totalSize <= t0->base.heap->size ) {
419 u0 = t0;
420 u1 = t1;
421 } else if ( t1->base.memBlock && totalSize <= t1->base.heap->size ) {
422 u0 = t1;
423 u1 = t0;
424 }
425
426 if ( u0 ) {
427 heap = u0->heap;
428
429 ret = mach64AllocateMultiTex( mmesa, u0, u1, heap, GL_FALSE );
430 } else {
431 /* Both textures are swapped out or collocation is impossible */
432 u0 = t0;
433 u1 = t1;
434
435 /* Choose the heap appropriately */
436 heap = MACH64_CARD_HEAP;
437
438 if ( totalSize > mmesa->texture_heaps[heap]->size ) {
439 heap = MACH64_AGP_HEAP;
440 }
441
442 ret = mach64AllocateMultiTex( mmesa, u0, u1, heap, GL_TRUE );
443 }
444
445 if ( ret == -1 && heap == MACH64_CARD_HEAP ) {
446 /* Try AGP if local memory failed */
447 heap = MACH64_AGP_HEAP;
448
449 ret = mach64AllocateMultiTex( mmesa, u0, u1, heap, GL_TRUE );
450 }
451
452 if ( ret == -1 ) {
453 /* FIXME:
454 * Swap out all textures from the AGP heap and re-run allocation, this
455 * should succeed in all cases.
456 */
457 fprintf( stderr, "%s: upload multi-texture failure, sz0=%d sz1=%d\n",
458 __FUNCTION__, t0->base.totalSize, t1->base.totalSize );
459 exit(-1);
460 }
461
462 /* Set the base offset of the texture image */
463 t0->bufAddr = mmesa->mach64Screen->texOffset[heap] + t0->base.memBlock->ofs;
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 }