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