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