Merge branch 'mesa_7_7_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_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 t->bufAddr = mmesa->mach64Screen->texOffset[heap] + t->base.memBlock->ofs;
309
310 /* Force loading the new state into the hardware */
311 mmesa->dirty |= (MACH64_UPLOAD_SCALE_3D_CNTL |
312 MACH64_UPLOAD_TEXTURE);
313 }
314
315 /* Let the world know we've used this memory recently */
316 driUpdateTextureLRU( (driTextureObject *) t );
317
318 /* Upload any images that are new */
319 if ( t->base.dirty_images[0] ) {
320 const GLint j = t->base.tObj->BaseLevel;
321 if (t->heap == MACH64_AGP_HEAP) {
322 /* Need to make sure any vertex buffers in the queue complete */
323 mach64WaitForIdleLocked( mmesa );
324 mach64UploadAGPSubImage( mmesa, t, j, 0, 0,
325 t->base.tObj->Image[0][j]->Width,
326 t->base.tObj->Image[0][j]->Height );
327 } else {
328 mach64UploadLocalSubImage( mmesa, t, j, 0, 0,
329 t->base.tObj->Image[0][j]->Width,
330 t->base.tObj->Image[0][j]->Height );
331 }
332
333 mmesa->setup.tex_cntl |= MACH64_TEX_CACHE_FLUSH;
334 t->base.dirty_images[0] = 0;
335 }
336
337 mmesa->dirty |= MACH64_UPLOAD_TEXTURE;
338 }
339
340
341 /* Allocate memory from the same texture heap `heap' for both textures
342 * `u0' and `u1'.
343 */
344 static int mach64AllocateMultiTex( mach64ContextPtr mmesa,
345 mach64TexObjPtr u0,
346 mach64TexObjPtr u1,
347 int heap, GLboolean alloc_u0 )
348 {
349 /* Both objects should be bound */
350 assert( u0->base.bound && u1->base.bound );
351
352 if ( alloc_u0 ) {
353 /* Evict u0 from its current heap */
354 if ( u0->base.memBlock ) {
355 assert( u0->heap != heap );
356 driSwapOutTextureObject( (driTextureObject *) u0 );
357 }
358
359 /* Try to allocate u0 in the chosen heap */
360 u0->heap = driAllocateTexture( &mmesa->texture_heaps[heap], 1,
361 (driTextureObject *) u0 );
362
363 if ( u0->heap == -1 ) {
364 return -1;
365 }
366 }
367
368 /* Evict u1 from its current heap */
369 if ( u1->base.memBlock ) {
370 assert( u1->heap != heap );
371 driSwapOutTextureObject( (driTextureObject *) u1 );
372 }
373
374 /* Try to allocate u1 in the same heap as u0 */
375 u1->heap = driAllocateTexture( &mmesa->texture_heaps[heap], 1,
376 (driTextureObject *) u1 );
377
378 if ( u1->heap == -1 ) {
379 return -1;
380 }
381
382 /* Bound objects are not evicted */
383 assert( u0->base.memBlock && u1->base.memBlock );
384 assert( u0->heap == u1->heap );
385
386 return heap;
387 }
388
389 /* The mach64 needs to have both primary and secondary textures in either
390 * local or AGP memory, so we need a "buddy system" to make sure that allocation
391 * succeeds or fails for both textures.
392 */
393 void mach64UploadMultiTexImages( mach64ContextPtr mmesa,
394 mach64TexObjPtr t0,
395 mach64TexObjPtr t1 )
396 {
397 if ( MACH64_DEBUG & DEBUG_VERBOSE_API ) {
398 fprintf( stderr, "%s( %p, %p %p )\n",
399 __FUNCTION__, mmesa->glCtx, t0, t1 );
400 }
401
402 assert(t0 && t1);
403 assert(t0->base.tObj && t1->base.tObj);
404
405 if ( !t0->base.memBlock || !t1->base.memBlock || t0->heap != t1->heap ) {
406 mach64TexObjPtr u0 = NULL;
407 mach64TexObjPtr u1 = NULL;
408 unsigned totalSize = t0->base.totalSize + t1->base.totalSize;
409
410 int heap, ret;
411
412 /* Check if one of the textures is already swapped in a heap and the
413 * other texture fits in that heap.
414 */
415 if ( t0->base.memBlock && totalSize <= t0->base.heap->size ) {
416 u0 = t0;
417 u1 = t1;
418 } else if ( t1->base.memBlock && totalSize <= t1->base.heap->size ) {
419 u0 = t1;
420 u1 = t0;
421 }
422
423 if ( u0 ) {
424 heap = u0->heap;
425
426 ret = mach64AllocateMultiTex( mmesa, u0, u1, heap, GL_FALSE );
427 } else {
428 /* Both textures are swapped out or collocation is impossible */
429 u0 = t0;
430 u1 = t1;
431
432 /* Choose the heap appropriately */
433 heap = MACH64_CARD_HEAP;
434
435 if ( totalSize > mmesa->texture_heaps[heap]->size ) {
436 heap = MACH64_AGP_HEAP;
437 }
438
439 ret = mach64AllocateMultiTex( mmesa, u0, u1, heap, GL_TRUE );
440 }
441
442 if ( ret == -1 && heap == MACH64_CARD_HEAP ) {
443 /* Try AGP if local memory failed */
444 heap = MACH64_AGP_HEAP;
445
446 ret = mach64AllocateMultiTex( mmesa, u0, u1, heap, GL_TRUE );
447 }
448
449 if ( ret == -1 ) {
450 /* FIXME:
451 * Swap out all textures from the AGP heap and re-run allocation, this
452 * should succeed in all cases.
453 */
454 fprintf( stderr, "%s: upload multi-texture failure, sz0=%d sz1=%d\n",
455 __FUNCTION__, t0->base.totalSize, t1->base.totalSize );
456 exit(-1);
457 }
458
459 /* Set the base offset of the texture image */
460 t0->bufAddr = mmesa->mach64Screen->texOffset[heap] + t0->base.memBlock->ofs;
461 t1->bufAddr = mmesa->mach64Screen->texOffset[heap] + t1->base.memBlock->ofs;
462
463 /* Force loading the new state into the hardware */
464 mmesa->dirty |= (MACH64_UPLOAD_SCALE_3D_CNTL |
465 MACH64_UPLOAD_TEXTURE);
466 }
467
468 /* Let the world know we've used this memory recently */
469 driUpdateTextureLRU( (driTextureObject *) t0 );
470 driUpdateTextureLRU( (driTextureObject *) t1 );
471
472 /* Upload any images that are new */
473 if ( t0->base.dirty_images[0] ) {
474 const GLint j0 = t0->base.tObj->BaseLevel;
475 if (t0->heap == MACH64_AGP_HEAP) {
476 /* Need to make sure any vertex buffers in the queue complete */
477 mach64WaitForIdleLocked( mmesa );
478 mach64UploadAGPSubImage( mmesa, t0, j0, 0, 0,
479 t0->base.tObj->Image[0][j0]->Width,
480 t0->base.tObj->Image[0][j0]->Height );
481 } else {
482 mach64UploadLocalSubImage( mmesa, t0, j0, 0, 0,
483 t0->base.tObj->Image[0][j0]->Width,
484 t0->base.tObj->Image[0][j0]->Height );
485 }
486 mmesa->setup.tex_cntl |= MACH64_TEX_CACHE_FLUSH;
487 t0->base.dirty_images[0] = 0;
488 }
489 if ( t1->base.dirty_images[0] ) {
490 const GLint j1 = t1->base.tObj->BaseLevel;
491 if (t1->heap == MACH64_AGP_HEAP) {
492 /* Need to make sure any vertex buffers in the queue complete */
493 mach64WaitForIdleLocked( mmesa );
494 mach64UploadAGPSubImage( mmesa, t1, j1, 0, 0,
495 t1->base.tObj->Image[0][j1]->Width,
496 t1->base.tObj->Image[0][j1]->Height );
497 } else {
498 mach64UploadLocalSubImage( mmesa, t1, j1, 0, 0,
499 t1->base.tObj->Image[0][j1]->Width,
500 t1->base.tObj->Image[0][j1]->Height );
501 }
502
503 mmesa->setup.tex_cntl |= MACH64_TEX_CACHE_FLUSH;
504 t1->base.dirty_images[0] = 0;
505 }
506
507 mmesa->dirty |= MACH64_UPLOAD_TEXTURE;
508 }