include surface.offset in address calculations
[mesa.git] / src / mesa / pipe / softpipe / sp_tex_sample.c
1 /**************************************************************************
2 *
3 * Copyright 2007 Tungsten Graphics, Inc., Cedar Park, Texas.
4 * All Rights Reserved.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the
8 * "Software"), to deal in the Software without restriction, including
9 * without limitation the rights to use, copy, modify, merge, publish,
10 * distribute, sub license, and/or sell copies of the Software, and to
11 * permit persons to whom the Software is furnished to do so, subject to
12 * the following conditions:
13 *
14 * The above copyright notice and this permission notice (including the
15 * next paragraph) shall be included in all copies or substantial portions
16 * of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
21 * IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS BE LIABLE FOR
22 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
23 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
24 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 *
26 **************************************************************************/
27
28 /**
29 * Texture sampling
30 *
31 * Authors:
32 * Brian Paul
33 */
34
35
36 #include "main/macros.h"
37 #include "sp_context.h"
38 #include "sp_surface.h"
39 #include "sp_tex_sample.h"
40 #include "pipe/p_context.h"
41 #include "pipe/p_defines.h"
42 #include "pipe/tgsi/core/tgsi_exec.h"
43
44
45 /*
46 * Note, the FRAC macro has to work perfectly. Otherwise you'll sometimes
47 * see 1-pixel bands of improperly weighted linear-filtered textures.
48 * The tests/texwrap.c demo is a good test.
49 * Also note, FRAC(x) doesn't truly return the fractional part of x for x < 0.
50 * Instead, if x < 0 then FRAC(x) = 1 - true_frac(x).
51 */
52 #define FRAC(f) ((f) - IFLOOR(f))
53
54
55 /**
56 * Linear interpolation macro
57 */
58 #define LERP(T, A, B) ( (A) + (T) * ((B) - (A)) )
59
60
61 /**
62 * Do 2D/biliner interpolation of float values.
63 * v00, v10, v01 and v11 are typically four texture samples in a square/box.
64 * a and b are the horizontal and vertical interpolants.
65 * It's important that this function is inlined when compiled with
66 * optimization! If we find that's not true on some systems, convert
67 * to a macro.
68 */
69 static INLINE GLfloat
70 lerp_2d(GLfloat a, GLfloat b,
71 GLfloat v00, GLfloat v10, GLfloat v01, GLfloat v11)
72 {
73 const GLfloat temp0 = LERP(a, v00, v10);
74 const GLfloat temp1 = LERP(a, v01, v11);
75 return LERP(b, temp0, temp1);
76 }
77
78
79 /**
80 * Compute the remainder of a divided by b, but be careful with
81 * negative values so that REPEAT mode works right.
82 */
83 static INLINE GLint
84 repeat_remainder(GLint a, GLint b)
85 {
86 if (a >= 0)
87 return a % b;
88 else
89 return (a + 1) % b + b - 1;
90 }
91
92
93 /**
94 * Apply texture coord wrapping mode and return integer texture index.
95 * \param wrapMode PIPE_TEX_WRAP_x
96 * \param s the texcoord
97 * \param size the texture image size
98 * \return integer texture index
99 */
100 static INLINE GLint
101 nearest_texcoord(GLuint wrapMode, GLfloat s, GLuint size)
102 {
103 GLint i;
104 switch (wrapMode) {
105 case PIPE_TEX_WRAP_REPEAT:
106 /* s limited to [0,1) */
107 /* i limited to [0,size-1] */
108 i = IFLOOR(s * size);
109 i = repeat_remainder(i, size);
110 return i;
111 case PIPE_TEX_WRAP_CLAMP:
112 /* s limited to [0,1] */
113 /* i limited to [0,size-1] */
114 if (s <= 0.0F)
115 i = 0;
116 else if (s >= 1.0F)
117 i = size - 1;
118 else
119 i = IFLOOR(s * size);
120 return i;
121 case PIPE_TEX_WRAP_CLAMP_TO_EDGE:
122 {
123 /* s limited to [min,max] */
124 /* i limited to [0, size-1] */
125 const GLfloat min = 1.0F / (2.0F * size);
126 const GLfloat max = 1.0F - min;
127 if (s < min)
128 i = 0;
129 else if (s > max)
130 i = size - 1;
131 else
132 i = IFLOOR(s * size);
133 }
134 return i;
135 case PIPE_TEX_WRAP_CLAMP_TO_BORDER:
136 {
137 /* s limited to [min,max] */
138 /* i limited to [-1, size] */
139 const GLfloat min = -1.0F / (2.0F * size);
140 const GLfloat max = 1.0F - min;
141 if (s <= min)
142 i = -1;
143 else if (s >= max)
144 i = size;
145 else
146 i = IFLOOR(s * size);
147 }
148 return i;
149 case PIPE_TEX_WRAP_MIRROR_REPEAT:
150 {
151 const GLfloat min = 1.0F / (2.0F * size);
152 const GLfloat max = 1.0F - min;
153 const GLint flr = IFLOOR(s);
154 GLfloat u;
155 if (flr & 1)
156 u = 1.0F - (s - (GLfloat) flr);
157 else
158 u = s - (GLfloat) flr;
159 if (u < min)
160 i = 0;
161 else if (u > max)
162 i = size - 1;
163 else
164 i = IFLOOR(u * size);
165 }
166 return i;
167 case PIPE_TEX_WRAP_MIRROR_CLAMP:
168 {
169 /* s limited to [0,1] */
170 /* i limited to [0,size-1] */
171 const GLfloat u = FABSF(s);
172 if (u <= 0.0F)
173 i = 0;
174 else if (u >= 1.0F)
175 i = size - 1;
176 else
177 i = IFLOOR(u * size);
178 }
179 return i;
180 case PIPE_TEX_WRAP_MIRROR_CLAMP_TO_EDGE:
181 {
182 /* s limited to [min,max] */
183 /* i limited to [0, size-1] */
184 const GLfloat min = 1.0F / (2.0F * size);
185 const GLfloat max = 1.0F - min;
186 const GLfloat u = FABSF(s);
187 if (u < min)
188 i = 0;
189 else if (u > max)
190 i = size - 1;
191 else
192 i = IFLOOR(u * size);
193 }
194 return i;
195 case PIPE_TEX_WRAP_MIRROR_CLAMP_TO_BORDER:
196 {
197 /* s limited to [min,max] */
198 /* i limited to [0, size-1] */
199 const GLfloat min = -1.0F / (2.0F * size);
200 const GLfloat max = 1.0F - min;
201 const GLfloat u = FABSF(s);
202 if (u < min)
203 i = -1;
204 else if (u > max)
205 i = size;
206 else
207 i = IFLOOR(u * size);
208 }
209 return i;
210 default:
211 assert(0);
212 return 0;
213 }
214 }
215
216
217 /**
218 * Used to compute texel locations for linear sampling.
219 * \param wrapMode PIPE_TEX_WRAP_x
220 * \param s the texcoord
221 * \param size the texture image size
222 * \param i0 returns first texture index
223 * \param i1 returns second texture index (usually *i0 + 1)
224 * \param a returns blend factor/weight between texture indexes
225 */
226 static INLINE void
227 linear_texcoord(GLuint wrapMode, GLfloat s, GLuint size,
228 GLint *i0, GLint *i1, GLfloat *a)
229 {
230 GLfloat u;
231 switch (wrapMode) {
232 case PIPE_TEX_WRAP_REPEAT:
233 u = s * size - 0.5F;
234 *i0 = repeat_remainder(IFLOOR(u), size);
235 *i1 = repeat_remainder(*i0 + 1, size);
236 break;
237 case PIPE_TEX_WRAP_CLAMP:
238 if (s <= 0.0F)
239 u = 0.0F;
240 else if (s >= 1.0F)
241 u = (GLfloat) size;
242 else
243 u = s * size;
244 u -= 0.5F;
245 *i0 = IFLOOR(u);
246 *i1 = *i0 + 1;
247 break;
248 case PIPE_TEX_WRAP_CLAMP_TO_EDGE:
249 if (s <= 0.0F)
250 u = 0.0F;
251 else if (s >= 1.0F)
252 u = (GLfloat) size;
253 else
254 u = s * size;
255 u -= 0.5F;
256 *i0 = IFLOOR(u);
257 *i1 = *i0 + 1;
258 if (*i0 < 0)
259 *i0 = 0;
260 if (*i1 >= (GLint) size)
261 *i1 = size - 1;
262 break;
263 case PIPE_TEX_WRAP_CLAMP_TO_BORDER:
264 {
265 const GLfloat min = -1.0F / (2.0F * size);
266 const GLfloat max = 1.0F - min;
267 if (s <= min)
268 u = min * size;
269 else if (s >= max)
270 u = max * size;
271 else
272 u = s * size;
273 u -= 0.5F;
274 *i0 = IFLOOR(u);
275 *i1 = *i0 + 1;
276 }
277 break;
278 case PIPE_TEX_WRAP_MIRROR_REPEAT:
279 {
280 const GLint flr = IFLOOR(s);
281 if (flr & 1)
282 u = 1.0F - (s - (GLfloat) flr);
283 else
284 u = s - (GLfloat) flr;
285 u = (u * size) - 0.5F;
286 *i0 = IFLOOR(u);
287 *i1 = *i0 + 1;
288 if (*i0 < 0)
289 *i0 = 0;
290 if (*i1 >= (GLint) size)
291 *i1 = size - 1;
292 }
293 break;
294 case PIPE_TEX_WRAP_MIRROR_CLAMP:
295 u = FABSF(s);
296 if (u >= 1.0F)
297 u = (GLfloat) size;
298 else
299 u *= size;
300 u -= 0.5F;
301 *i0 = IFLOOR(u);
302 *i1 = *i0 + 1;
303 break;
304 case PIPE_TEX_WRAP_MIRROR_CLAMP_TO_EDGE:
305 u = FABSF(s);
306 if (u >= 1.0F)
307 u = (GLfloat) size;
308 else
309 u *= size;
310 u -= 0.5F;
311 *i0 = IFLOOR(u);
312 *i1 = *i0 + 1;
313 if (*i0 < 0)
314 *i0 = 0;
315 if (*i1 >= (GLint) size)
316 *i1 = size - 1;
317 break;
318 case PIPE_TEX_WRAP_MIRROR_CLAMP_TO_BORDER:
319 {
320 const GLfloat min = -1.0F / (2.0F * size);
321 const GLfloat max = 1.0F - min;
322 u = FABSF(s);
323 if (u <= min)
324 u = min * size;
325 else if (u >= max)
326 u = max * size;
327 else
328 u *= size;
329 u -= 0.5F;
330 *i0 = IFLOOR(u);
331 *i1 = *i0 + 1;
332 }
333 break;
334 default:
335 assert(0);
336 }
337 *a = FRAC(u);
338 }
339
340
341 static GLuint
342 choose_cube_face(const GLfloat texcoord[4], GLfloat newCoord[4])
343 {
344 /*
345 major axis
346 direction target sc tc ma
347 ---------- ------------------------------- --- --- ---
348 +rx TEXTURE_CUBE_MAP_POSITIVE_X_EXT -rz -ry rx
349 -rx TEXTURE_CUBE_MAP_NEGATIVE_X_EXT +rz -ry rx
350 +ry TEXTURE_CUBE_MAP_POSITIVE_Y_EXT +rx +rz ry
351 -ry TEXTURE_CUBE_MAP_NEGATIVE_Y_EXT +rx -rz ry
352 +rz TEXTURE_CUBE_MAP_POSITIVE_Z_EXT +rx -ry rz
353 -rz TEXTURE_CUBE_MAP_NEGATIVE_Z_EXT -rx -ry rz
354 */
355 const GLfloat rx = texcoord[0];
356 const GLfloat ry = texcoord[1];
357 const GLfloat rz = texcoord[2];
358 const GLfloat arx = FABSF(rx), ary = FABSF(ry), arz = FABSF(rz);
359 GLuint face;
360 GLfloat sc, tc, ma;
361
362 if (arx > ary && arx > arz) {
363 if (rx >= 0.0F) {
364 face = PIPE_TEX_FACE_POS_X;
365 sc = -rz;
366 tc = -ry;
367 ma = arx;
368 }
369 else {
370 face = PIPE_TEX_FACE_NEG_X;
371 sc = rz;
372 tc = -ry;
373 ma = arx;
374 }
375 }
376 else if (ary > arx && ary > arz) {
377 if (ry >= 0.0F) {
378 face = PIPE_TEX_FACE_POS_Y;
379 sc = rx;
380 tc = rz;
381 ma = ary;
382 }
383 else {
384 face = PIPE_TEX_FACE_NEG_Y;
385 sc = rx;
386 tc = -rz;
387 ma = ary;
388 }
389 }
390 else {
391 if (rz > 0.0F) {
392 face = PIPE_TEX_FACE_POS_Z;
393 sc = rx;
394 tc = -ry;
395 ma = arz;
396 }
397 else {
398 face = PIPE_TEX_FACE_NEG_Z;
399 sc = -rx;
400 tc = -ry;
401 ma = arz;
402 }
403 }
404
405 newCoord[0] = ( sc / ma + 1.0F ) * 0.5F;
406 newCoord[1] = ( tc / ma + 1.0F ) * 0.5F;
407
408 return face;
409 }
410
411
412 static void
413 sp_get_sample_1d(struct tgsi_sampler *sampler,
414 const GLfloat strq[4], GLfloat lambda, GLfloat rgba[4])
415 {
416 struct pipe_context *pipe = (struct pipe_context *) sampler->pipe;
417 struct pipe_surface *ps
418 = pipe->get_tex_surface(pipe, sampler->texture, 0, 0, 0);
419
420 switch (sampler->state->min_img_filter) {
421 case PIPE_TEX_FILTER_NEAREST:
422 {
423 GLint x;
424 x = nearest_texcoord(sampler->state->wrap_s, strq[0],
425 sampler->texture->width0);
426 ps->get_tile(ps, x, 0, 1, 1, rgba);
427 }
428 break;
429 case PIPE_TEX_FILTER_LINEAR:
430 {
431 GLfloat t0[4], t1[4];
432 GLint x0, x1;
433 GLfloat a;
434 linear_texcoord(sampler->state->wrap_s, strq[0],
435 sampler->texture->width0, &x0, &x1, &a);
436 ps->get_tile(ps, x0, 0, 1, 1, t0);
437 ps->get_tile(ps, x1, 0, 1, 1, t1);
438
439 rgba[0] = LERP(a, t0[0], t1[0]);
440 rgba[1] = LERP(a, t0[1], t1[1]);
441 rgba[2] = LERP(a, t0[2], t1[2]);
442 rgba[3] = LERP(a, t0[3], t1[3]);
443 }
444 break;
445 default:
446 assert(0);
447 }
448 }
449
450 static GLuint
451 choose_mipmap_level(struct tgsi_sampler *sampler, GLfloat lambda)
452 {
453 if (sampler->state->min_mip_filter == PIPE_TEX_MIPFILTER_NONE) {
454 return 0;
455 }
456 else {
457 GLint level = (int) lambda;
458 level = CLAMP(level, sampler->texture->first_level,
459 sampler->texture->last_level);
460 return level;
461 }
462 }
463
464
465 /**
466 * Called via tgsi_sampler::get_sample()
467 * Use the sampler's state setting to get a filtered RGBA value
468 * from the sampler's texture (mipmap tree).
469 *
470 * XXX we can implement many versions of this function, each
471 * tightly coded for a specific combination of sampler state
472 * (nearest + repeat), (bilinear mipmap + clamp), etc.
473 *
474 * The update_samplers() function in st_atom_sampler.c could create
475 * a new tgsi_sampler object for each state combo it finds....
476 */
477 static void
478 sp_get_sample_2d(struct tgsi_sampler *sampler,
479 const GLfloat strq[4], GLfloat lambda, GLfloat rgba[4])
480 {
481 struct pipe_context *pipe = (struct pipe_context *) sampler->pipe;
482 GLuint filter;
483 GLint level0;
484
485 if (lambda < 0.0)
486 filter = sampler->state->mag_img_filter;
487 else
488 filter = sampler->state->min_img_filter;
489
490 level0 = choose_mipmap_level(sampler, lambda);
491
492 switch (filter) {
493 case PIPE_TEX_FILTER_NEAREST:
494 {
495 GLint x = nearest_texcoord(sampler->state->wrap_s, strq[0],
496 sampler->texture->level[level0].width);
497 GLint y = nearest_texcoord(sampler->state->wrap_t, strq[1],
498 sampler->texture->level[level0].height);
499 GLint cx = x / SAMPLER_CACHE_SIZE;
500 GLint cy = y / SAMPLER_CACHE_SIZE;
501 if (cx != sampler->cache_x || cy != sampler->cache_y ||
502 level0 != sampler->cache_level) {
503 /* cache miss, replace cache with new tile */
504 struct pipe_surface *ps
505 = pipe->get_tex_surface(pipe, sampler->texture, 0, level0, 0);
506 assert(ps->width == sampler->texture->level[level0].width);
507 assert(ps->height == sampler->texture->level[level0].height);
508 sampler->cache_level = level0;
509 sampler->cache_x = cx;
510 sampler->cache_y = cy;
511 ps->get_tile(ps,
512 cx * SAMPLER_CACHE_SIZE,
513 cy * SAMPLER_CACHE_SIZE,
514 SAMPLER_CACHE_SIZE, SAMPLER_CACHE_SIZE,
515 (GLfloat *) sampler->cache);
516 /*printf("cache miss (%d, %d)\n", x, y);*/
517 }
518 else {
519 /*printf("cache hit (%d, %d)\n", x, y);*/
520 }
521 /* get texel from cache */
522 cx = x % SAMPLER_CACHE_SIZE;
523 cy = y % SAMPLER_CACHE_SIZE;
524 COPY_4V(rgba, sampler->cache[cy][cx]);
525 }
526 break;
527 case PIPE_TEX_FILTER_LINEAR:
528 {
529 GLfloat t00[4], t01[4], t10[4], t11[4];
530 GLint x0, y0, x1, y1;
531 GLfloat a, b;
532 struct pipe_surface *ps
533 = pipe->get_tex_surface(pipe, sampler->texture, 0, level0, 0);
534
535 linear_texcoord(sampler->state->wrap_s, strq[0],
536 sampler->texture->width0, &x0, &x1, &a);
537 linear_texcoord(sampler->state->wrap_t, strq[1],
538 sampler->texture->height0, &y0, &y1, &b);
539 ps->get_tile(ps, x0, y0, 1, 1, t00);
540 ps->get_tile(ps, x1, y0, 1, 1, t10);
541 ps->get_tile(ps, x0, y1, 1, 1, t01);
542 ps->get_tile(ps, x1, y1, 1, 1, t11);
543
544 rgba[0] = lerp_2d(a, b, t00[0], t10[0], t01[0], t11[0]);
545 rgba[1] = lerp_2d(a, b, t00[1], t10[1], t01[1], t11[1]);
546 rgba[2] = lerp_2d(a, b, t00[2], t10[2], t01[2], t11[2]);
547 rgba[3] = lerp_2d(a, b, t00[3], t10[3], t01[3], t11[3]);
548 }
549 break;
550 /*
551 {
552 GLuint level0, level1;
553 level0 = choose_mipmap_level(sampler, lambda);
554 }
555 break;
556 */
557 default:
558 assert(0);
559 }
560 }
561
562
563 static void
564 sp_get_sample_3d(struct tgsi_sampler *sampler,
565 const GLfloat strq[4], GLfloat lamba, GLfloat rgba[4])
566 {
567 /* get/map pipe_surfaces corresponding to 3D tex slices */
568 }
569
570
571 static void
572 sp_get_sample_cube(struct tgsi_sampler *sampler,
573 const GLfloat strq[4], GLfloat lambda, GLfloat rgba[4])
574 {
575 GLfloat st[4];
576 GLuint face = choose_cube_face(strq, st);
577
578 /* get/map surface corresponding to the face */
579 }
580
581
582 void
583 sp_get_sample(struct tgsi_sampler *sampler,
584 const GLfloat strq[4], GLfloat lambda, GLfloat rgba[4])
585 {
586 switch (sampler->texture->target) {
587 case GL_TEXTURE_1D:
588 sp_get_sample_1d(sampler, strq, lambda, rgba);
589 break;
590 case GL_TEXTURE_2D:
591 sp_get_sample_2d(sampler, strq, lambda, rgba);
592 break;
593 case GL_TEXTURE_3D:
594 sp_get_sample_3d(sampler, strq, lambda, rgba);
595 break;
596 case GL_TEXTURE_CUBE_MAP:
597 sp_get_sample_cube(sampler, strq, lambda, rgba);
598 break;
599 default:
600 assert(0);
601 }
602 }
603