fixes for CHAN_BITS!=8
[mesa.git] / src / mesa / swrast / s_accum.c
1 /*
2 * Mesa 3-D graphics library
3 * Version: 6.5.1
4 *
5 * Copyright (C) 1999-2006 Brian Paul 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 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
11 * and/or sell copies of the Software, and to permit persons to whom the
12 * Software is furnished to do so, subject to the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be included
15 * in all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
18 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20 * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
21 * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 */
24
25
26 #include "glheader.h"
27 #include "context.h"
28 #include "macros.h"
29 #include "imports.h"
30 #include "fbobject.h"
31
32 #include "s_accum.h"
33 #include "s_context.h"
34 #include "s_masking.h"
35 #include "s_span.h"
36
37
38 /* XXX this would have to change for accum buffers with more or less
39 * than 16 bits per color channel.
40 */
41 #define ACCUM_SCALE16 32767.0
42
43
44 /*
45 * Accumulation buffer notes
46 *
47 * Normally, accumulation buffer values are GLshorts with values in
48 * [-32767, 32767] which represent floating point colors in [-1, 1],
49 * as defined by the OpenGL specification.
50 *
51 * We optimize for the common case used for full-scene antialiasing:
52 * // start with accum buffer cleared to zero
53 * glAccum(GL_LOAD, w); // or GL_ACCUM the first image
54 * glAccum(GL_ACCUM, w);
55 * ...
56 * glAccum(GL_ACCUM, w);
57 * glAccum(GL_RETURN, 1.0);
58 * That is, we start with an empty accumulation buffer and accumulate
59 * n images, each with weight w = 1/n.
60 * In this scenario, we can simply store unscaled integer values in
61 * the accum buffer instead of scaled integers. We'll also keep track
62 * of the w value so when we do GL_RETURN we simply divide the accumulated
63 * values by n (n=1/w).
64 * This lets us avoid _many_ int->float->int conversions.
65 */
66
67
68 #if CHAN_BITS == 8
69 /* enable the optimization */
70 #define USE_OPTIMIZED_ACCUM 1
71 #else
72 #define USE_OPTIMIZED_ACCUM 0
73 #endif
74
75
76 /**
77 * This is called when we fall out of optimized/unscaled accum buffer mode.
78 * That is, we convert each unscaled accum buffer value into a scaled value
79 * representing the range[-1, 1].
80 */
81 static void
82 rescale_accum( GLcontext *ctx )
83 {
84 SWcontext *swrast = SWRAST_CONTEXT(ctx);
85 struct gl_renderbuffer *rb
86 = ctx->DrawBuffer->Attachment[BUFFER_ACCUM].Renderbuffer;
87 const GLfloat s = swrast->_IntegerAccumScaler * (32767.0F / CHAN_MAXF);
88
89 assert(rb);
90 assert(rb->_BaseFormat == GL_RGBA);
91 /* add other types in future? */
92 assert(rb->DataType == GL_SHORT || rb->DataType == GL_UNSIGNED_SHORT);
93 assert(swrast->_IntegerAccumMode);
94
95 if (rb->GetPointer(ctx, rb, 0, 0)) {
96 /* directly-addressable memory */
97 GLuint y;
98 for (y = 0; y < rb->Height; y++) {
99 GLuint i;
100 GLshort *acc = (GLshort *) rb->GetPointer(ctx, rb, 0, y);
101 for (i = 0; i < 4 * rb->Width; i++) {
102 acc[i] = (GLshort) (acc[i] * s);
103 }
104 }
105 }
106 else {
107 /* use get/put row funcs */
108 GLuint y;
109 for (y = 0; y < rb->Height; y++) {
110 GLshort accRow[MAX_WIDTH * 4];
111 GLuint i;
112 rb->GetRow(ctx, rb, rb->Width, 0, y, accRow);
113 for (i = 0; i < 4 * rb->Width; i++) {
114 accRow[i] = (GLshort) (accRow[i] * s);
115 }
116 rb->PutRow(ctx, rb, rb->Width, 0, y, accRow, NULL);
117 }
118 }
119
120 swrast->_IntegerAccumMode = GL_FALSE;
121 }
122
123
124
125 /**
126 * Clear the accumulation Buffer.
127 */
128 void
129 _swrast_clear_accum_buffer( GLcontext *ctx, struct gl_renderbuffer *rb )
130 {
131 SWcontext *swrast = SWRAST_CONTEXT(ctx);
132 GLuint x, y, width, height;
133
134 if (ctx->Visual.accumRedBits == 0) {
135 /* No accumulation buffer! Not an error. */
136 return;
137 }
138
139 assert(rb);
140 assert(rb->_BaseFormat == GL_RGBA);
141 /* add other types in future? */
142 assert(rb->DataType == GL_SHORT || rb->DataType == GL_UNSIGNED_SHORT);
143
144 /* bounds, with scissor */
145 x = ctx->DrawBuffer->_Xmin;
146 y = ctx->DrawBuffer->_Ymin;
147 width = ctx->DrawBuffer->_Xmax - ctx->DrawBuffer->_Xmin;
148 height = ctx->DrawBuffer->_Ymax - ctx->DrawBuffer->_Ymin;
149
150 if (rb->DataType == GL_SHORT || rb->DataType == GL_UNSIGNED_SHORT) {
151 const GLfloat accScale = 32767.0;
152 GLshort clearVal[4];
153 GLuint i;
154
155 clearVal[0] = (GLshort) (ctx->Accum.ClearColor[0] * accScale);
156 clearVal[1] = (GLshort) (ctx->Accum.ClearColor[1] * accScale);
157 clearVal[2] = (GLshort) (ctx->Accum.ClearColor[2] * accScale);
158 clearVal[3] = (GLshort) (ctx->Accum.ClearColor[3] * accScale);
159
160 for (i = 0; i < height; i++) {
161 rb->PutMonoRow(ctx, rb, width, x, y + i, clearVal, NULL);
162 }
163 }
164 else {
165 /* someday support other sizes */
166 }
167
168 /* update optimized accum state vars */
169 if (ctx->Accum.ClearColor[0] == 0.0 && ctx->Accum.ClearColor[1] == 0.0 &&
170 ctx->Accum.ClearColor[2] == 0.0 && ctx->Accum.ClearColor[3] == 0.0) {
171 #if USE_OPTIMIZED_ACCUM
172 swrast->_IntegerAccumMode = GL_TRUE;
173 #else
174 swrast->_IntegerAccumMode = GL_FALSE;
175 #endif
176 swrast->_IntegerAccumScaler = 0.0; /* denotes empty accum buffer */
177 }
178 else {
179 swrast->_IntegerAccumMode = GL_FALSE;
180 }
181 }
182
183
184 static void
185 accum_add(GLcontext *ctx, GLfloat value,
186 GLint xpos, GLint ypos, GLint width, GLint height )
187 {
188 SWcontext *swrast = SWRAST_CONTEXT(ctx);
189 struct gl_renderbuffer *rb
190 = ctx->DrawBuffer->Attachment[BUFFER_ACCUM].Renderbuffer;
191
192 assert(rb);
193
194 /* Leave optimized accum buffer mode */
195 if (swrast->_IntegerAccumMode)
196 rescale_accum(ctx);
197
198 if (rb->DataType == GL_SHORT || rb->DataType == GL_UNSIGNED_SHORT) {
199 const GLshort incr = (GLshort) (value * ACCUM_SCALE16);
200 if (rb->GetPointer(ctx, rb, 0, 0)) {
201 GLint i, j;
202 for (i = 0; i < height; i++) {
203 GLshort *acc = (GLshort *) rb->GetPointer(ctx, rb, xpos, ypos + i);
204 for (j = 0; j < 4 * width; j++) {
205 acc[j] += incr;
206 }
207 }
208 }
209 else {
210 GLint i, j;
211 for (i = 0; i < height; i++) {
212 GLshort accRow[4 * MAX_WIDTH];
213 rb->GetRow(ctx, rb, width, xpos, ypos + i, accRow);
214 for (j = 0; j < 4 * width; j++) {
215 accRow[j] += incr;
216 }
217 rb->PutRow(ctx, rb, width, xpos, ypos + i, accRow, NULL);
218 }
219 }
220 }
221 else {
222 /* other types someday */
223 }
224 }
225
226
227 static void
228 accum_mult(GLcontext *ctx, GLfloat mult,
229 GLint xpos, GLint ypos, GLint width, GLint height )
230 {
231 SWcontext *swrast = SWRAST_CONTEXT(ctx);
232 struct gl_renderbuffer *rb
233 = ctx->DrawBuffer->Attachment[BUFFER_ACCUM].Renderbuffer;
234
235 assert(rb);
236
237 /* Leave optimized accum buffer mode */
238 if (swrast->_IntegerAccumMode)
239 rescale_accum(ctx);
240
241 if (rb->DataType == GL_SHORT || rb->DataType == GL_UNSIGNED_SHORT) {
242 if (rb->GetPointer(ctx, rb, 0, 0)) {
243 GLint i, j;
244 for (i = 0; i < height; i++) {
245 GLshort *acc = (GLshort *) rb->GetPointer(ctx, rb, xpos, ypos + i);
246 for (j = 0; j < 4 * width; j++) {
247 acc[j] = (GLshort) (acc[j] * mult);
248 }
249 }
250 }
251 else {
252 GLint i, j;
253 for (i = 0; i < height; i++) {
254 GLshort accRow[4 * MAX_WIDTH];
255 rb->GetRow(ctx, rb, width, xpos, ypos + i, accRow);
256 for (j = 0; j < 4 * width; j++) {
257 accRow[j] = (GLshort) (accRow[j] * mult);
258 }
259 rb->PutRow(ctx, rb, width, xpos, ypos + i, accRow, NULL);
260 }
261 }
262 }
263 else {
264 /* other types someday */
265 }
266 }
267
268
269
270 static void
271 accum_accum(GLcontext *ctx, GLfloat value,
272 GLint xpos, GLint ypos, GLint width, GLint height )
273 {
274 SWcontext *swrast = SWRAST_CONTEXT(ctx);
275 struct gl_renderbuffer *rb
276 = ctx->DrawBuffer->Attachment[BUFFER_ACCUM].Renderbuffer;
277 const GLboolean directAccess = (rb->GetPointer(ctx, rb, 0, 0) != NULL);
278
279 assert(rb);
280
281 if (!ctx->ReadBuffer->_ColorReadBuffer) {
282 /* no read buffer - OK */
283 return;
284 }
285
286 /* May have to leave optimized accum buffer mode */
287 if (swrast->_IntegerAccumScaler == 0.0 && value > 0.0 && value <= 1.0)
288 swrast->_IntegerAccumScaler = value;
289 if (swrast->_IntegerAccumMode && value != swrast->_IntegerAccumScaler)
290 rescale_accum(ctx);
291
292 if (rb->DataType == GL_SHORT || rb->DataType == GL_UNSIGNED_SHORT) {
293 const GLfloat scale = value * ACCUM_SCALE16 / CHAN_MAXF;
294 GLshort accumRow[4 * MAX_WIDTH];
295 GLchan rgba[MAX_WIDTH][4];
296 GLint i;
297
298 for (i = 0; i < height; i++) {
299 GLshort *acc;
300 if (directAccess) {
301 acc = (GLshort *) rb->GetPointer(ctx, rb, xpos, ypos + i);
302 }
303 else {
304 rb->GetRow(ctx, rb, width, xpos, ypos + i, accumRow);
305 acc = accumRow;
306 }
307
308 /* read colors from color buffer */
309 _swrast_read_rgba_span(ctx, ctx->ReadBuffer->_ColorReadBuffer, width,
310 xpos, ypos + i, rgba);
311
312 /* do accumulation */
313 if (swrast->_IntegerAccumMode) {
314 /* simply add integer color values into accum buffer */
315 GLint j;
316 for (j = 0; j < width; j++) {
317 acc[j * 4 + 0] += rgba[j][RCOMP];
318 acc[j * 4 + 1] += rgba[j][GCOMP];
319 acc[j * 4 + 2] += rgba[j][BCOMP];
320 acc[j * 4 + 3] += rgba[j][ACOMP];
321 }
322 }
323 else {
324 /* scaled integer (or float) accum buffer */
325 GLint j;
326 for (j = 0; j < width; j++) {
327 acc[j * 4 + 0] += (GLshort) ((GLfloat) rgba[j][RCOMP] * scale);
328 acc[j * 4 + 1] += (GLshort) ((GLfloat) rgba[j][GCOMP] * scale);
329 acc[j * 4 + 2] += (GLshort) ((GLfloat) rgba[j][BCOMP] * scale);
330 acc[j * 4 + 3] += (GLshort) ((GLfloat) rgba[j][ACOMP] * scale);
331 }
332 }
333
334 if (!directAccess) {
335 rb->PutRow(ctx, rb, width, xpos, ypos + i, accumRow, NULL);
336 }
337 }
338 }
339 else {
340 /* other types someday */
341 }
342 }
343
344
345
346 static void
347 accum_load(GLcontext *ctx, GLfloat value,
348 GLint xpos, GLint ypos, GLint width, GLint height )
349 {
350 SWcontext *swrast = SWRAST_CONTEXT(ctx);
351 struct gl_renderbuffer *rb
352 = ctx->DrawBuffer->Attachment[BUFFER_ACCUM].Renderbuffer;
353 const GLboolean directAccess = (rb->GetPointer(ctx, rb, 0, 0) != NULL);
354
355 assert(rb);
356
357 if (!ctx->ReadBuffer->_ColorReadBuffer) {
358 /* no read buffer - OK */
359 return;
360 }
361
362 /* This is a change to go into optimized accum buffer mode */
363 if (value > 0.0 && value <= 1.0) {
364 #if USE_OPTIMIZED_ACCUM
365 swrast->_IntegerAccumMode = GL_TRUE;
366 #else
367 swrast->_IntegerAccumMode = GL_FALSE;
368 #endif
369 swrast->_IntegerAccumScaler = value;
370 }
371 else {
372 swrast->_IntegerAccumMode = GL_FALSE;
373 swrast->_IntegerAccumScaler = 0.0;
374 }
375
376 if (rb->DataType == GL_SHORT || rb->DataType == GL_UNSIGNED_SHORT) {
377 const GLfloat scale = value * ACCUM_SCALE16 / CHAN_MAXF;
378 GLshort accumRow[4 * MAX_WIDTH];
379 GLchan rgba[MAX_WIDTH][4];
380 GLint i;
381
382 for (i = 0; i < height; i++) {
383 GLshort *acc;
384 if (directAccess) {
385 acc = (GLshort *) rb->GetPointer(ctx, rb, xpos, ypos + i);
386 }
387 else {
388 rb->GetRow(ctx, rb, width, xpos, ypos + i, accumRow);
389 acc = accumRow;
390 }
391
392 /* read colors from color buffer */
393 _swrast_read_rgba_span(ctx, ctx->ReadBuffer->_ColorReadBuffer, width,
394 xpos, ypos + i, rgba);
395
396 /* do load */
397 if (swrast->_IntegerAccumMode) {
398 /* just copy values in */
399 GLint j;
400 assert(swrast->_IntegerAccumScaler > 0.0);
401 assert(swrast->_IntegerAccumScaler <= 1.0);
402 for (j = 0; j < width; j++) {
403 acc[j * 4 + 0] = rgba[j][RCOMP];
404 acc[j * 4 + 1] = rgba[j][GCOMP];
405 acc[j * 4 + 2] = rgba[j][BCOMP];
406 acc[j * 4 + 3] = rgba[j][ACOMP];
407 }
408 }
409 else {
410 /* scaled integer (or float) accum buffer */
411 GLint j;
412 for (j = 0; j < width; j++) {
413 acc[j * 4 + 0] = (GLshort) ((GLfloat) rgba[j][RCOMP] * scale);
414 acc[j * 4 + 1] = (GLshort) ((GLfloat) rgba[j][GCOMP] * scale);
415 acc[j * 4 + 2] = (GLshort) ((GLfloat) rgba[j][BCOMP] * scale);
416 acc[j * 4 + 3] = (GLshort) ((GLfloat) rgba[j][ACOMP] * scale);
417 }
418 }
419
420 if (!directAccess) {
421 rb->PutRow(ctx, rb, width, xpos, ypos + i, accumRow, NULL);
422 }
423 }
424 }
425 }
426
427
428 static void
429 accum_return(GLcontext *ctx, GLfloat value,
430 GLint xpos, GLint ypos, GLint width, GLint height )
431 {
432 SWcontext *swrast = SWRAST_CONTEXT(ctx);
433 struct gl_framebuffer *fb = ctx->DrawBuffer;
434 struct gl_renderbuffer *accumRb = fb->Attachment[BUFFER_ACCUM].Renderbuffer;
435 const GLboolean directAccess
436 = (accumRb->GetPointer(ctx, accumRb, 0, 0) != NULL);
437 const GLboolean masking = (!ctx->Color.ColorMask[RCOMP] ||
438 !ctx->Color.ColorMask[GCOMP] ||
439 !ctx->Color.ColorMask[BCOMP] ||
440 !ctx->Color.ColorMask[ACOMP]);
441
442 static GLchan multTable[32768];
443 static GLfloat prevMult = 0.0;
444 const GLfloat mult = swrast->_IntegerAccumScaler;
445 const GLint max = MIN2((GLint) (256 / mult), 32767);
446
447 /* May have to leave optimized accum buffer mode */
448 if (swrast->_IntegerAccumMode && value != 1.0)
449 rescale_accum(ctx);
450
451 if (swrast->_IntegerAccumMode && swrast->_IntegerAccumScaler > 0) {
452 /* build lookup table to avoid many floating point multiplies */
453 GLint j;
454 assert(swrast->_IntegerAccumScaler <= 1.0);
455 if (mult != prevMult) {
456 for (j = 0; j < max; j++)
457 multTable[j] = IROUND((GLfloat) j * mult);
458 prevMult = mult;
459 }
460 }
461
462 if (accumRb->DataType == GL_SHORT ||
463 accumRb->DataType == GL_UNSIGNED_SHORT) {
464 const GLfloat scale = value * CHAN_MAXF / ACCUM_SCALE16;
465 GLuint buffer;
466 GLint i;
467
468 /* XXX maybe transpose the 'i' and 'buffer' loops??? */
469 for (i = 0; i < height; i++) {
470 GLchan rgba[MAX_WIDTH][4];
471 GLshort accumRow[4 * MAX_WIDTH];
472 GLshort *acc;
473
474 if (directAccess) {
475 acc = (GLshort *) accumRb->GetPointer(ctx, accumRb, xpos, ypos +i);
476 }
477 else {
478 accumRb->GetRow(ctx, accumRb, width, xpos, ypos + i, accumRow);
479 acc = accumRow;
480 }
481
482 /* get the colors to return */
483 if (swrast->_IntegerAccumMode) {
484 GLint j;
485 for (j = 0; j < width; j++) {
486 ASSERT(acc[j * 4 + 0] < max);
487 ASSERT(acc[j * 4 + 1] < max);
488 ASSERT(acc[j * 4 + 2] < max);
489 ASSERT(acc[j * 4 + 3] < max);
490 rgba[j][RCOMP] = multTable[acc[j * 4 + 0]];
491 rgba[j][GCOMP] = multTable[acc[j * 4 + 1]];
492 rgba[j][BCOMP] = multTable[acc[j * 4 + 2]];
493 rgba[j][ACOMP] = multTable[acc[j * 4 + 3]];
494 }
495 }
496 else {
497 /* scaled integer (or float) accum buffer */
498 GLint j;
499 for (j = 0; j < width; j++) {
500 #if CHAN_BITS==32
501 GLchan r = acc[j * 4 + 0] * scale;
502 GLchan g = acc[j * 4 + 1] * scale;
503 GLchan b = acc[j * 4 + 2] * scale;
504 GLchan a = acc[j * 4 + 3] * scale;
505 #else
506 GLint r = IROUND( (GLfloat) (acc[j * 4 + 0]) * scale );
507 GLint g = IROUND( (GLfloat) (acc[j * 4 + 1]) * scale );
508 GLint b = IROUND( (GLfloat) (acc[j * 4 + 2]) * scale );
509 GLint a = IROUND( (GLfloat) (acc[j * 4 + 3]) * scale );
510 #endif
511 rgba[j][RCOMP] = CLAMP( r, 0, CHAN_MAX );
512 rgba[j][GCOMP] = CLAMP( g, 0, CHAN_MAX );
513 rgba[j][BCOMP] = CLAMP( b, 0, CHAN_MAX );
514 rgba[j][ACOMP] = CLAMP( a, 0, CHAN_MAX );
515 }
516 }
517
518 /* store colors */
519 for (buffer = 0; buffer < fb->_NumColorDrawBuffers[0]; buffer++) {
520 struct gl_renderbuffer *rb = fb->_ColorDrawBuffers[0][buffer];
521 if (masking) {
522 _swrast_mask_rgba_array(ctx, rb, width, xpos, ypos + i, rgba);
523 }
524 rb->PutRow(ctx, rb, width, xpos, ypos + i, rgba, NULL);
525 }
526 }
527 }
528 else {
529 /* other types someday */
530 }
531 }
532
533
534
535 /**
536 * Software fallback for glAccum.
537 */
538 void
539 _swrast_Accum( GLcontext *ctx, GLenum op, GLfloat value,
540 GLint xpos, GLint ypos,
541 GLint width, GLint height )
542
543 {
544 SWcontext *swrast = SWRAST_CONTEXT(ctx);
545
546 if (SWRAST_CONTEXT(ctx)->NewState)
547 _swrast_validate_derived( ctx );
548
549 if (!ctx->DrawBuffer->Attachment[BUFFER_ACCUM].Renderbuffer) {
550 _mesa_warning(ctx, "Calling glAccum() without an accumulation buffer");
551 return;
552 }
553
554 RENDER_START(swrast, ctx);
555
556 switch (op) {
557 case GL_ADD:
558 if (value != 0.0F) {
559 accum_add(ctx, value, xpos, ypos, width, height);
560 }
561 break;
562 case GL_MULT:
563 if (value != 1.0F) {
564 accum_mult(ctx, value, xpos, ypos, width, height);
565 }
566 break;
567 case GL_ACCUM:
568 if (value != 0.0F) {
569 accum_accum(ctx, value, xpos, ypos, width, height);
570 }
571 break;
572 case GL_LOAD:
573 accum_load(ctx, value, xpos, ypos, width, height);
574 break;
575 case GL_RETURN:
576 accum_return(ctx, value, xpos, ypos, width, height);
577 break;
578 default:
579 _mesa_problem(ctx, "invalid mode in _swrast_Accum()");
580 break;
581 }
582
583 RENDER_FINISH(swrast, ctx);
584 }