1 /* $Id: s_accum.c,v 1.14 2002/02/02 17:24:11 brianp Exp $ */
4 * Mesa 3-D graphics library
7 * Copyright (C) 1999-2002 Brian Paul All Rights Reserved.
9 * Permission is hereby granted, free of charge, to any person obtaining a
10 * copy of this software and associated documentation files (the "Software"),
11 * to deal in the Software without restriction, including without limitation
12 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
13 * and/or sell copies of the Software, and to permit persons to whom the
14 * Software is furnished to do so, subject to the following conditions:
16 * The above copyright notice and this permission notice shall be included
17 * in all copies or substantial portions of the Software.
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
20 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
22 * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
23 * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
24 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
35 #include "s_alphabuf.h"
36 #include "s_context.h"
37 #include "s_masking.h"
42 * Accumulation buffer notes
44 * Normally, accumulation buffer values are GLshorts with values in
45 * [-32767, 32767] which represent floating point colors in [-1, 1],
46 * as suggested by the OpenGL specification.
48 * We optimize for the common case used for full-scene antialiasing:
49 * // start with accum buffer cleared to zero
50 * glAccum(GL_LOAD, w); // or GL_ACCUM the first image
51 * glAccum(GL_ACCUM, w);
53 * glAccum(GL_ACCUM, w);
54 * glAccum(GL_RETURN, 1.0);
55 * That is, we start with an empty accumulation buffer and accumulate
56 * n images, each with weight w = 1/n.
57 * In this scenario, we can simply store unscaled integer values in
58 * the accum buffer instead of scaled integers. We'll also keep track
59 * of the w value so when we do GL_RETURN we simply divide the accumulated
61 * This lets us avoid _many_ int->float->int conversions.
66 #define USE_OPTIMIZED_ACCUM /* enable the optimization */
72 _mesa_alloc_accum_buffer( GLcontext
*ctx
)
74 SWcontext
*swrast
= SWRAST_CONTEXT(ctx
);
77 if (ctx
->DrawBuffer
->Accum
) {
78 FREE( ctx
->DrawBuffer
->Accum
);
79 ctx
->DrawBuffer
->Accum
= NULL
;
82 /* allocate accumulation buffer if not already present */
83 n
= ctx
->DrawBuffer
->Width
* ctx
->DrawBuffer
->Height
* 4 * sizeof(GLaccum
);
84 ctx
->DrawBuffer
->Accum
= (GLaccum
*) MALLOC( n
);
85 if (!ctx
->DrawBuffer
->Accum
) {
86 /* unable to setup accumulation buffer */
87 _mesa_error( ctx
, GL_OUT_OF_MEMORY
, "glAccum" );
89 #ifdef USE_OPTIMIZED_ACCUM
90 swrast
->_IntegerAccumMode
= GL_TRUE
;
92 swrast
->_IntegerAccumMode
= GL_FALSE
;
94 swrast
->_IntegerAccumScaler
= 0.0;
103 * This is called when we fall out of optimized/unscaled accum buffer mode.
104 * That is, we convert each unscaled accum buffer value into a scaled value
105 * representing the range[-1, 1].
107 static void rescale_accum( GLcontext
*ctx
)
109 SWcontext
*swrast
= SWRAST_CONTEXT(ctx
);
110 const GLuint n
= ctx
->DrawBuffer
->Width
* ctx
->DrawBuffer
->Height
* 4;
111 const GLfloat s
= swrast
->_IntegerAccumScaler
* (32767.0F
/ CHAN_MAXF
);
112 GLaccum
*accum
= ctx
->DrawBuffer
->Accum
;
115 assert(swrast
->_IntegerAccumMode
);
118 for (i
= 0; i
< n
; i
++) {
119 accum
[i
] = (GLaccum
) (accum
[i
] * s
);
122 swrast
->_IntegerAccumMode
= GL_FALSE
;
131 * Clear the accumulation Buffer.
134 _mesa_clear_accum_buffer( GLcontext
*ctx
)
136 SWcontext
*swrast
= SWRAST_CONTEXT(ctx
);
140 if (ctx
->Visual
.accumRedBits
==0) {
141 /* No accumulation buffer! */
145 if (sizeof(GLaccum
)==1) {
148 else if (sizeof(GLaccum
)==2) {
155 /* number of pixels */
156 buffersize
= ctx
->DrawBuffer
->Width
* ctx
->DrawBuffer
->Height
;
158 if (!ctx
->DrawBuffer
->Accum
) {
159 /* try to alloc accumulation buffer */
160 ctx
->DrawBuffer
->Accum
= (GLaccum
*)
161 MALLOC( buffersize
* 4 * sizeof(GLaccum
) );
164 if (ctx
->DrawBuffer
->Accum
) {
165 if (ctx
->Scissor
.Enabled
) {
166 /* Limit clear to scissor box */
167 const GLaccum r
= (GLaccum
) (ctx
->Accum
.ClearColor
[0] * acc_scale
);
168 const GLaccum g
= (GLaccum
) (ctx
->Accum
.ClearColor
[1] * acc_scale
);
169 const GLaccum b
= (GLaccum
) (ctx
->Accum
.ClearColor
[2] * acc_scale
);
170 const GLaccum a
= (GLaccum
) (ctx
->Accum
.ClearColor
[3] * acc_scale
);
174 /* size of region to clear */
175 width
= 4 * (ctx
->DrawBuffer
->_Xmax
- ctx
->DrawBuffer
->_Xmin
);
176 height
= ctx
->DrawBuffer
->_Ymax
- ctx
->DrawBuffer
->_Ymin
;
177 /* ptr to first element to clear */
178 row
= ctx
->DrawBuffer
->Accum
179 + 4 * (ctx
->DrawBuffer
->_Ymin
* ctx
->DrawBuffer
->Width
180 + ctx
->DrawBuffer
->_Xmin
);
181 for (j
=0;j
<height
;j
++) {
182 for (i
=0;i
<width
;i
+=4) {
188 row
+= 4 * ctx
->DrawBuffer
->Width
;
192 /* clear whole buffer */
193 if (ctx
->Accum
.ClearColor
[0]==0.0 &&
194 ctx
->Accum
.ClearColor
[1]==0.0 &&
195 ctx
->Accum
.ClearColor
[2]==0.0 &&
196 ctx
->Accum
.ClearColor
[3]==0.0) {
198 BZERO( ctx
->DrawBuffer
->Accum
, buffersize
* 4 * sizeof(GLaccum
) );
202 const GLaccum r
= (GLaccum
) (ctx
->Accum
.ClearColor
[0] * acc_scale
);
203 const GLaccum g
= (GLaccum
) (ctx
->Accum
.ClearColor
[1] * acc_scale
);
204 const GLaccum b
= (GLaccum
) (ctx
->Accum
.ClearColor
[2] * acc_scale
);
205 const GLaccum a
= (GLaccum
) (ctx
->Accum
.ClearColor
[3] * acc_scale
);
206 GLaccum
*acc
= ctx
->DrawBuffer
->Accum
;
208 for (i
=0;i
<buffersize
;i
++) {
217 /* update optimized accum state vars */
218 if (ctx
->Accum
.ClearColor
[0] == 0.0 && ctx
->Accum
.ClearColor
[1] == 0.0 &&
219 ctx
->Accum
.ClearColor
[2] == 0.0 && ctx
->Accum
.ClearColor
[3] == 0.0) {
220 #ifdef USE_OPTIMIZED_ACCUM
221 swrast
->_IntegerAccumMode
= GL_TRUE
;
223 swrast
->_IntegerAccumMode
= GL_FALSE
;
225 swrast
->_IntegerAccumScaler
= 0.0; /* denotes empty accum buffer */
228 swrast
->_IntegerAccumMode
= GL_FALSE
;
235 _swrast_Accum( GLcontext
*ctx
, GLenum op
, GLfloat value
,
236 GLint xpos
, GLint ypos
,
237 GLint width
, GLint height
)
240 SWcontext
*swrast
= SWRAST_CONTEXT(ctx
);
243 GLchan rgba
[MAX_WIDTH
][4];
244 const GLuint colorMask
= *((GLuint
*) &ctx
->Color
.ColorMask
);
247 if (SWRAST_CONTEXT(ctx
)->NewState
)
248 _swrast_validate_derived( ctx
);
250 if (!ctx
->DrawBuffer
->Accum
) {
252 "Calling glAccum() without an accumulation "
253 "buffer (low memory?)");
257 if (sizeof(GLaccum
)==1) {
260 else if (sizeof(GLaccum
)==2) {
272 const GLaccum val
= (GLaccum
) (value
* acc_scale
);
274 /* Leave optimized accum buffer mode */
275 if (swrast
->_IntegerAccumMode
)
277 for (j
= 0; j
< height
; j
++) {
278 GLaccum
*acc
= ctx
->DrawBuffer
->Accum
+ ypos
* width4
+ 4*xpos
;
280 for (i
= 0; i
< width4
; i
++) {
291 /* Leave optimized accum buffer mode */
292 if (swrast
->_IntegerAccumMode
)
294 for (j
= 0; j
< height
; j
++) {
295 GLaccum
*acc
= ctx
->DrawBuffer
->Accum
+ ypos
* width4
+ 4 * xpos
;
297 for (i
= 0; i
< width4
; i
++) {
298 acc
[i
] = (GLaccum
) ( (GLfloat
) acc
[i
] * value
);
309 (*swrast
->Driver
.SetReadBuffer
)( ctx
, ctx
->ReadBuffer
,
310 ctx
->Pixel
.DriverReadBuffer
);
312 /* May have to leave optimized accum buffer mode */
313 if (swrast
->_IntegerAccumScaler
== 0.0 && value
> 0.0 && value
<= 1.0)
314 swrast
->_IntegerAccumScaler
= value
;
315 if (swrast
->_IntegerAccumMode
&& value
!= swrast
->_IntegerAccumScaler
)
318 RENDER_START(swrast
,ctx
);
320 if (swrast
->_IntegerAccumMode
) {
321 /* simply add integer color values into accum buffer */
323 GLaccum
*acc
= ctx
->DrawBuffer
->Accum
+ ypos
* width4
+ xpos
* 4;
324 assert(swrast
->_IntegerAccumScaler
> 0.0);
325 assert(swrast
->_IntegerAccumScaler
<= 1.0);
326 for (j
= 0; j
< height
; j
++) {
329 _mesa_read_rgba_span(ctx
, ctx
->DrawBuffer
, width
, xpos
, ypos
, rgba
);
330 for (i
= i4
= 0; i
< width
; i
++, i4
+=4) {
331 acc
[i4
+0] += rgba
[i
][RCOMP
];
332 acc
[i4
+1] += rgba
[i
][GCOMP
];
333 acc
[i4
+2] += rgba
[i
][BCOMP
];
334 acc
[i4
+3] += rgba
[i
][ACOMP
];
341 /* scaled integer (or float) accum buffer */
342 const GLfloat rscale
= value
* acc_scale
/ CHAN_MAXF
;
343 const GLfloat gscale
= value
* acc_scale
/ CHAN_MAXF
;
344 const GLfloat bscale
= value
* acc_scale
/ CHAN_MAXF
;
345 const GLfloat ascale
= value
* acc_scale
/ CHAN_MAXF
;
347 for (j
=0;j
<height
;j
++) {
348 GLaccum
*acc
= ctx
->DrawBuffer
->Accum
+ ypos
* width4
+ xpos
* 4;
350 _mesa_read_rgba_span(ctx
, ctx
->DrawBuffer
, width
, xpos
, ypos
, rgba
);
351 for (i
=0;i
<width
;i
++) {
352 acc
[0] += (GLaccum
) ( (GLfloat
) rgba
[i
][RCOMP
] * rscale
);
353 acc
[1] += (GLaccum
) ( (GLfloat
) rgba
[i
][GCOMP
] * gscale
);
354 acc
[2] += (GLaccum
) ( (GLfloat
) rgba
[i
][BCOMP
] * bscale
);
355 acc
[3] += (GLaccum
) ( (GLfloat
) rgba
[i
][ACOMP
] * ascale
);
361 /* restore read buffer = draw buffer (the default) */
362 (*swrast
->Driver
.SetReadBuffer
)( ctx
, ctx
->DrawBuffer
,
363 ctx
->Color
.DriverDrawBuffer
);
364 RENDER_FINISH(swrast
,ctx
);
368 (*swrast
->Driver
.SetReadBuffer
)( ctx
, ctx
->ReadBuffer
,
369 ctx
->Pixel
.DriverReadBuffer
);
371 /* This is a change to go into optimized accum buffer mode */
372 if (value
> 0.0 && value
<= 1.0) {
373 #ifdef USE_OPTIMIZED_ACCUM
374 swrast
->_IntegerAccumMode
= GL_TRUE
;
376 swrast
->_IntegerAccumMode
= GL_FALSE
;
378 swrast
->_IntegerAccumScaler
= value
;
381 swrast
->_IntegerAccumMode
= GL_FALSE
;
382 swrast
->_IntegerAccumScaler
= 0.0;
385 RENDER_START(swrast
,ctx
);
386 if (swrast
->_IntegerAccumMode
) {
387 /* just copy values into accum buffer */
389 GLaccum
*acc
= ctx
->DrawBuffer
->Accum
+ ypos
* width4
+ xpos
* 4;
390 assert(swrast
->_IntegerAccumScaler
> 0.0);
391 assert(swrast
->_IntegerAccumScaler
<= 1.0);
392 for (j
= 0; j
< height
; j
++) {
394 _mesa_read_rgba_span(ctx
, ctx
->DrawBuffer
, width
, xpos
, ypos
, rgba
);
395 for (i
= i4
= 0; i
< width
; i
++, i4
+= 4) {
396 acc
[i4
+0] = rgba
[i
][RCOMP
];
397 acc
[i4
+1] = rgba
[i
][GCOMP
];
398 acc
[i4
+2] = rgba
[i
][BCOMP
];
399 acc
[i4
+3] = rgba
[i
][ACOMP
];
406 /* scaled integer (or float) accum buffer */
407 const GLfloat rscale
= value
* acc_scale
/ CHAN_MAXF
;
408 const GLfloat gscale
= value
* acc_scale
/ CHAN_MAXF
;
409 const GLfloat bscale
= value
* acc_scale
/ CHAN_MAXF
;
410 const GLfloat ascale
= value
* acc_scale
/ CHAN_MAXF
;
412 const GLfloat d
= 3.0 / acc_scale
; /* XXX what's this? */
415 for (j
= 0; j
< height
; j
++) {
416 GLaccum
*acc
= ctx
->DrawBuffer
->Accum
+ ypos
* width4
+ xpos
* 4;
417 _mesa_read_rgba_span(ctx
, ctx
->DrawBuffer
, width
, xpos
, ypos
, rgba
);
418 for (i
=0;i
<width
;i
++) {
420 *acc
++ = (GLaccum
) ((GLfloat
) rgba
[i
][RCOMP
] * rscale
+ d
);
421 *acc
++ = (GLaccum
) ((GLfloat
) rgba
[i
][GCOMP
] * gscale
+ d
);
422 *acc
++ = (GLaccum
) ((GLfloat
) rgba
[i
][BCOMP
] * bscale
+ d
);
423 *acc
++ = (GLaccum
) ((GLfloat
) rgba
[i
][ACOMP
] * ascale
+ d
);
425 *acc
++ = (GLaccum
) ((GLfloat
) rgba
[i
][RCOMP
] * rscale
);
426 *acc
++ = (GLaccum
) ((GLfloat
) rgba
[i
][GCOMP
] * gscale
);
427 *acc
++ = (GLaccum
) ((GLfloat
) rgba
[i
][BCOMP
] * bscale
);
428 *acc
++ = (GLaccum
) ((GLfloat
) rgba
[i
][ACOMP
] * ascale
);
435 /* restore read buffer = draw buffer (the default) */
436 (*swrast
->Driver
.SetReadBuffer
)( ctx
, ctx
->DrawBuffer
,
437 ctx
->Color
.DriverDrawBuffer
);
438 RENDER_FINISH(swrast
,ctx
);
442 /* May have to leave optimized accum buffer mode */
443 if (swrast
->_IntegerAccumMode
&& value
!= 1.0)
446 RENDER_START(swrast
,ctx
);
447 #ifdef USE_OPTIMIZED_ACCUM
448 if (swrast
->_IntegerAccumMode
&& swrast
->_IntegerAccumScaler
> 0) {
449 /* build lookup table to avoid many floating point multiplies */
450 static GLchan multTable
[32768];
451 static GLfloat prevMult
= 0.0;
452 const GLfloat mult
= swrast
->_IntegerAccumScaler
;
453 const GLint max
= MIN2((GLint
) (256 / mult
), 32767);
455 if (mult
!= prevMult
) {
456 for (j
= 0; j
< max
; j
++)
457 multTable
[j
] = IROUND((GLfloat
) j
* mult
);
461 assert(swrast
->_IntegerAccumScaler
> 0.0);
462 assert(swrast
->_IntegerAccumScaler
<= 1.0);
463 for (j
= 0; j
< height
; j
++) {
464 const GLaccum
*acc
= ctx
->DrawBuffer
->Accum
+ ypos
* width4
+ xpos
*4;
466 for (i
= i4
= 0; i
< width
; i
++, i4
+= 4) {
467 ASSERT(acc
[i4
+0] < max
);
468 ASSERT(acc
[i4
+1] < max
);
469 ASSERT(acc
[i4
+2] < max
);
470 ASSERT(acc
[i4
+3] < max
);
471 rgba
[i
][RCOMP
] = multTable
[acc
[i4
+0]];
472 rgba
[i
][GCOMP
] = multTable
[acc
[i4
+1]];
473 rgba
[i
][BCOMP
] = multTable
[acc
[i4
+2]];
474 rgba
[i
][ACOMP
] = multTable
[acc
[i4
+3]];
476 if (colorMask
!= 0xffffffff) {
477 _mesa_mask_rgba_array( ctx
, width
, xpos
, ypos
, rgba
);
479 (*swrast
->Driver
.WriteRGBASpan
)( ctx
, width
, xpos
, ypos
,
480 (const GLchan (*)[4])rgba
, NULL
);
481 if (ctx
->DrawBuffer
->UseSoftwareAlphaBuffers
482 && ctx
->Color
.ColorMask
[ACOMP
]) {
483 _mesa_write_alpha_span(ctx
, width
, xpos
, ypos
,
484 (CONST
GLchan (*)[4]) rgba
, NULL
);
490 #endif /* USE_OPTIMIZED_ACCUM */
492 /* scaled integer (or float) accum buffer */
493 const GLfloat rscale
= value
/ acc_scale
* CHAN_MAXF
;
494 const GLfloat gscale
= value
/ acc_scale
* CHAN_MAXF
;
495 const GLfloat bscale
= value
/ acc_scale
* CHAN_MAXF
;
496 const GLfloat ascale
= value
/ acc_scale
* CHAN_MAXF
;
498 for (j
=0;j
<height
;j
++) {
499 const GLaccum
*acc
= ctx
->DrawBuffer
->Accum
+ ypos
* width4
+ xpos
*4;
500 for (i
=0;i
<width
;i
++) {
501 GLint r
= IROUND( (GLfloat
) (acc
[0]) * rscale
);
502 GLint g
= IROUND( (GLfloat
) (acc
[1]) * gscale
);
503 GLint b
= IROUND( (GLfloat
) (acc
[2]) * bscale
);
504 GLint a
= IROUND( (GLfloat
) (acc
[3]) * ascale
);
506 rgba
[i
][RCOMP
] = CLAMP( r
, 0, CHAN_MAX
);
507 rgba
[i
][GCOMP
] = CLAMP( g
, 0, CHAN_MAX
);
508 rgba
[i
][BCOMP
] = CLAMP( b
, 0, CHAN_MAX
);
509 rgba
[i
][ACOMP
] = CLAMP( a
, 0, CHAN_MAX
);
511 if (colorMask
!= 0xffffffff) {
512 _mesa_mask_rgba_array( ctx
, width
, xpos
, ypos
, rgba
);
514 (*swrast
->Driver
.WriteRGBASpan
)( ctx
, width
, xpos
, ypos
,
515 (const GLchan (*)[4])rgba
, NULL
);
516 if (ctx
->DrawBuffer
->UseSoftwareAlphaBuffers
517 && ctx
->Color
.ColorMask
[ACOMP
]) {
518 _mesa_write_alpha_span(ctx
, width
, xpos
, ypos
,
519 (CONST
GLchan (*)[4]) rgba
, NULL
);
524 RENDER_FINISH(swrast
,ctx
);
528 _mesa_error( ctx
, GL_INVALID_ENUM
, "glAccum" );