1 /* $Id: s_accum.c,v 1.6 2001/03/07 05:06:12 brianp Exp $ */
4 * Mesa 3-D graphics library
7 * Copyright (C) 1999-2001 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.
33 #include "s_alphabuf.h"
34 #include "s_context.h"
35 #include "s_masking.h"
40 * Accumulation buffer notes
42 * Normally, accumulation buffer values are GLshorts with values in
43 * [-32767, 32767] which represent floating point colors in [-1, 1],
44 * as suggested by the OpenGL specification.
46 * We optimize for the common case used for full-scene antialiasing:
47 * // start with accum buffer cleared to zero
48 * glAccum(GL_LOAD, w); // or GL_ACCUM the first image
49 * glAccum(GL_ACCUM, w);
51 * glAccum(GL_ACCUM, w);
52 * glAccum(GL_RETURN, 1.0);
53 * That is, we start with an empty accumulation buffer and accumulate
54 * n images, each with weight w = 1/n.
55 * In this scenario, we can simply store unscaled integer values in
56 * the accum buffer instead of scaled integers. We'll also keep track
57 * of the w value so when we do GL_RETURN we simply divide the accumulated
59 * This lets us avoid _many_ int->float->int conversions.
64 #define USE_OPTIMIZED_ACCUM /* enable the optimization */
70 _mesa_alloc_accum_buffer( GLcontext
*ctx
)
72 SWcontext
*swrast
= SWRAST_CONTEXT(ctx
);
75 if (ctx
->DrawBuffer
->Accum
) {
76 FREE( ctx
->DrawBuffer
->Accum
);
77 ctx
->DrawBuffer
->Accum
= NULL
;
80 /* allocate accumulation buffer if not already present */
81 n
= ctx
->DrawBuffer
->Width
* ctx
->DrawBuffer
->Height
* 4 * sizeof(GLaccum
);
82 ctx
->DrawBuffer
->Accum
= (GLaccum
*) MALLOC( n
);
83 if (!ctx
->DrawBuffer
->Accum
) {
84 /* unable to setup accumulation buffer */
85 _mesa_error( ctx
, GL_OUT_OF_MEMORY
, "glAccum" );
87 #ifdef USE_OPTIMIZED_ACCUM
88 swrast
->_IntegerAccumMode
= GL_TRUE
;
90 swrast
->_IntegerAccumMode
= GL_FALSE
;
92 swrast
->_IntegerAccumScaler
= 0.0;
101 * This is called when we fall out of optimized/unscaled accum buffer mode.
102 * That is, we convert each unscaled accum buffer value into a scaled value
103 * representing the range[-1, 1].
105 static void rescale_accum( GLcontext
*ctx
)
107 SWcontext
*swrast
= SWRAST_CONTEXT(ctx
);
108 const GLuint n
= ctx
->DrawBuffer
->Width
* ctx
->DrawBuffer
->Height
* 4;
109 const GLfloat fChanMax
= (1 << (sizeof(GLchan
) * 8)) - 1;
110 const GLfloat s
= swrast
->_IntegerAccumScaler
* (32767.0 / fChanMax
);
111 GLaccum
*accum
= ctx
->DrawBuffer
->Accum
;
114 assert(swrast
->_IntegerAccumMode
);
117 for (i
= 0; i
< n
; i
++) {
118 accum
[i
] = (GLaccum
) (accum
[i
] * s
);
121 swrast
->_IntegerAccumMode
= GL_FALSE
;
130 * Clear the accumulation Buffer.
133 _mesa_clear_accum_buffer( GLcontext
*ctx
)
135 SWcontext
*swrast
= SWRAST_CONTEXT(ctx
);
139 if (ctx
->Visual
.accumRedBits
==0) {
140 /* No accumulation buffer! */
144 if (sizeof(GLaccum
)==1) {
147 else if (sizeof(GLaccum
)==2) {
151 /* sizeof(GLaccum) > 2 (Cray) */
152 acc_scale
= (float) SHRT_MAX
;
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 */
171 r
= (GLaccum
) (ctx
->Accum
.ClearColor
[0] * acc_scale
);
172 g
= (GLaccum
) (ctx
->Accum
.ClearColor
[1] * acc_scale
);
173 b
= (GLaccum
) (ctx
->Accum
.ClearColor
[2] * acc_scale
);
174 a
= (GLaccum
) (ctx
->Accum
.ClearColor
[3] * acc_scale
);
175 /* size of region to clear */
176 width
= 4 * (ctx
->DrawBuffer
->_Xmax
- ctx
->DrawBuffer
->_Xmin
);
177 height
= ctx
->DrawBuffer
->_Ymax
- ctx
->DrawBuffer
->_Ymin
;
178 /* ptr to first element to clear */
179 row
= ctx
->DrawBuffer
->Accum
180 + 4 * (ctx
->DrawBuffer
->_Ymin
* ctx
->DrawBuffer
->Width
181 + ctx
->DrawBuffer
->_Xmin
);
182 for (j
=0;j
<height
;j
++) {
183 for (i
=0;i
<width
;i
+=4) {
189 row
+= 4 * ctx
->DrawBuffer
->Width
;
193 /* clear whole buffer */
194 if (ctx
->Accum
.ClearColor
[0]==0.0 &&
195 ctx
->Accum
.ClearColor
[1]==0.0 &&
196 ctx
->Accum
.ClearColor
[2]==0.0 &&
197 ctx
->Accum
.ClearColor
[3]==0.0) {
199 BZERO( ctx
->DrawBuffer
->Accum
, buffersize
* 4 * sizeof(GLaccum
) );
203 GLaccum
*acc
, r
, g
, b
, a
;
206 acc
= ctx
->DrawBuffer
->Accum
;
207 r
= (GLaccum
) (ctx
->Accum
.ClearColor
[0] * acc_scale
);
208 g
= (GLaccum
) (ctx
->Accum
.ClearColor
[1] * acc_scale
);
209 b
= (GLaccum
) (ctx
->Accum
.ClearColor
[2] * acc_scale
);
210 a
= (GLaccum
) (ctx
->Accum
.ClearColor
[3] * acc_scale
);
211 for (i
=0;i
<buffersize
;i
++) {
220 /* update optimized accum state vars */
221 if (ctx
->Accum
.ClearColor
[0] == 0.0 && ctx
->Accum
.ClearColor
[1] == 0.0 &&
222 ctx
->Accum
.ClearColor
[2] == 0.0 && ctx
->Accum
.ClearColor
[3] == 0.0) {
223 #ifdef USE_OPTIMIZED_ACCUM
224 swrast
->_IntegerAccumMode
= GL_TRUE
;
226 swrast
->_IntegerAccumMode
= GL_FALSE
;
228 swrast
->_IntegerAccumScaler
= 0.0; /* denotes empty accum buffer */
231 swrast
->_IntegerAccumMode
= GL_FALSE
;
238 _swrast_Accum( GLcontext
*ctx
, GLenum op
, GLfloat value
,
239 GLint xpos
, GLint ypos
,
240 GLint width
, GLint height
)
243 SWcontext
*swrast
= SWRAST_CONTEXT(ctx
);
246 GLchan rgba
[MAX_WIDTH
][4];
247 const GLuint colorMask
= *((GLuint
*) &ctx
->Color
.ColorMask
);
248 const GLint iChanMax
= (1 << (sizeof(GLchan
) * 8)) - 1;
249 const GLfloat fChanMax
= (1 << (sizeof(GLchan
) * 8)) - 1;
252 if (SWRAST_CONTEXT(ctx
)->NewState
)
253 _swrast_validate_derived( ctx
);
255 if (!ctx
->DrawBuffer
->Accum
) {
257 "Calling glAccum() without an accumulation "
258 "buffer (low memory?)");
262 if (sizeof(GLaccum
)==1) {
265 else if (sizeof(GLaccum
)==2) {
269 /* sizeof(GLaccum) > 2 (Cray) */
270 acc_scale
= (float) SHRT_MAX
;
278 const GLaccum intVal
= (GLaccum
) (value
* acc_scale
);
280 /* Leave optimized accum buffer mode */
281 if (swrast
->_IntegerAccumMode
)
283 for (j
= 0; j
< height
; j
++) {
284 GLaccum
* acc
= ctx
->DrawBuffer
->Accum
+ ypos
* width4
+ 4 * xpos
;
286 for (i
= 0; i
< width4
; i
++) {
297 /* Leave optimized accum buffer mode */
298 if (swrast
->_IntegerAccumMode
)
300 for (j
= 0; j
< height
; j
++) {
301 GLaccum
*acc
= ctx
->DrawBuffer
->Accum
+ ypos
* width4
+ 4 * xpos
;
303 for (i
= 0; i
< width4
; i
++) {
304 acc
[i
] = (GLaccum
) ( (GLfloat
) acc
[i
] * value
);
315 (*ctx
->Driver
.SetReadBuffer
)( ctx
, ctx
->ReadBuffer
,
316 ctx
->Pixel
.DriverReadBuffer
);
318 /* May have to leave optimized accum buffer mode */
319 if (swrast
->_IntegerAccumScaler
== 0.0 && value
> 0.0 && value
<= 1.0)
320 swrast
->_IntegerAccumScaler
= value
;
321 if (swrast
->_IntegerAccumMode
&& value
!= swrast
->_IntegerAccumScaler
)
326 if (swrast
->_IntegerAccumMode
) {
327 /* simply add integer color values into accum buffer */
329 GLaccum
*acc
= ctx
->DrawBuffer
->Accum
+ ypos
* width4
+ xpos
* 4;
330 assert(swrast
->_IntegerAccumScaler
> 0.0);
331 assert(swrast
->_IntegerAccumScaler
<= 1.0);
332 for (j
= 0; j
< height
; j
++) {
335 _mesa_read_rgba_span(ctx
, ctx
->DrawBuffer
, width
, xpos
, ypos
, rgba
);
336 for (i
= i4
= 0; i
< width
; i
++, i4
+=4) {
337 acc
[i4
+0] += rgba
[i
][RCOMP
];
338 acc
[i4
+1] += rgba
[i
][GCOMP
];
339 acc
[i4
+2] += rgba
[i
][BCOMP
];
340 acc
[i4
+3] += rgba
[i
][ACOMP
];
347 /* scaled integer accum buffer */
348 const GLfloat rscale
= value
* acc_scale
/ fChanMax
;
349 const GLfloat gscale
= value
* acc_scale
/ fChanMax
;
350 const GLfloat bscale
= value
* acc_scale
/ fChanMax
;
351 const GLfloat ascale
= value
* acc_scale
/ fChanMax
;
353 for (j
=0;j
<height
;j
++) {
354 GLaccum
*acc
= ctx
->DrawBuffer
->Accum
+ ypos
* width4
+ xpos
* 4;
356 _mesa_read_rgba_span(ctx
, ctx
->DrawBuffer
, width
, xpos
, ypos
, rgba
);
357 for (i
=0;i
<width
;i
++) {
358 *acc
+= (GLaccum
) ( (GLfloat
) rgba
[i
][RCOMP
] * rscale
); acc
++;
359 *acc
+= (GLaccum
) ( (GLfloat
) rgba
[i
][GCOMP
] * gscale
); acc
++;
360 *acc
+= (GLaccum
) ( (GLfloat
) rgba
[i
][BCOMP
] * bscale
); acc
++;
361 *acc
+= (GLaccum
) ( (GLfloat
) rgba
[i
][ACOMP
] * ascale
); acc
++;
366 /* restore read buffer = draw buffer (the default) */
367 (*ctx
->Driver
.SetReadBuffer
)( ctx
, ctx
->DrawBuffer
,
368 ctx
->Color
.DriverDrawBuffer
);
373 (*ctx
->Driver
.SetReadBuffer
)( ctx
, ctx
->ReadBuffer
,
374 ctx
->Pixel
.DriverReadBuffer
);
376 /* This is a change to go into optimized accum buffer mode */
377 if (value
> 0.0 && value
<= 1.0) {
378 #ifdef USE_OPTIMIZED_ACCUM
379 swrast
->_IntegerAccumMode
= GL_TRUE
;
381 swrast
->_IntegerAccumMode
= GL_FALSE
;
383 swrast
->_IntegerAccumScaler
= value
;
386 swrast
->_IntegerAccumMode
= GL_FALSE
;
387 swrast
->_IntegerAccumScaler
= 0.0;
391 if (swrast
->_IntegerAccumMode
) {
392 /* just copy values into accum buffer */
394 GLaccum
*acc
= ctx
->DrawBuffer
->Accum
+ ypos
* width4
+ xpos
* 4;
395 assert(swrast
->_IntegerAccumScaler
> 0.0);
396 assert(swrast
->_IntegerAccumScaler
<= 1.0);
397 for (j
= 0; j
< height
; j
++) {
399 _mesa_read_rgba_span(ctx
, ctx
->DrawBuffer
, width
, xpos
, ypos
, rgba
);
400 for (i
= i4
= 0; i
< width
; i
++, i4
+= 4) {
401 acc
[i4
+0] = rgba
[i
][RCOMP
];
402 acc
[i4
+1] = rgba
[i
][GCOMP
];
403 acc
[i4
+2] = rgba
[i
][BCOMP
];
404 acc
[i4
+3] = rgba
[i
][ACOMP
];
411 /* scaled integer accum buffer */
412 const GLfloat rscale
= value
* acc_scale
/ fChanMax
;
413 const GLfloat gscale
= value
* acc_scale
/ fChanMax
;
414 const GLfloat bscale
= value
* acc_scale
/ fChanMax
;
415 const GLfloat ascale
= value
* acc_scale
/ fChanMax
;
416 const GLfloat d
= 3.0 / acc_scale
;
418 for (j
= 0; j
< height
; j
++) {
419 GLaccum
*acc
= ctx
->DrawBuffer
->Accum
+ ypos
* width4
+ xpos
* 4;
420 _mesa_read_rgba_span(ctx
, ctx
->DrawBuffer
, width
, xpos
, ypos
, rgba
);
421 for (i
=0;i
<width
;i
++) {
422 *acc
++ = (GLaccum
) ((GLfloat
) rgba
[i
][RCOMP
] * rscale
+ d
);
423 *acc
++ = (GLaccum
) ((GLfloat
) rgba
[i
][GCOMP
] * gscale
+ d
);
424 *acc
++ = (GLaccum
) ((GLfloat
) rgba
[i
][BCOMP
] * bscale
+ d
);
425 *acc
++ = (GLaccum
) ((GLfloat
) rgba
[i
][ACOMP
] * ascale
+ d
);
431 /* restore read buffer = draw buffer (the default) */
432 (*ctx
->Driver
.SetReadBuffer
)( ctx
, ctx
->DrawBuffer
,
433 ctx
->Color
.DriverDrawBuffer
);
438 /* May have to leave optimized accum buffer mode */
439 if (swrast
->_IntegerAccumMode
&& value
!= 1.0)
443 if (swrast
->_IntegerAccumMode
&& swrast
->_IntegerAccumScaler
> 0) {
444 /* build lookup table to avoid many floating point multiplies */
445 static GLchan multTable
[32768];
446 static GLfloat prevMult
= 0.0;
447 const GLfloat mult
= swrast
->_IntegerAccumScaler
;
448 const GLint max
= MIN2((GLint
) (256 / mult
), 32767);
450 if (mult
!= prevMult
) {
451 for (j
= 0; j
< max
; j
++)
452 multTable
[j
] = (GLint
) ((GLfloat
) j
* mult
+ 0.5F
);
456 assert(swrast
->_IntegerAccumScaler
> 0.0);
457 assert(swrast
->_IntegerAccumScaler
<= 1.0);
458 for (j
= 0; j
< height
; j
++) {
459 const GLaccum
*acc
= ctx
->DrawBuffer
->Accum
+ ypos
* width4
+ xpos
*4;
461 for (i
= i4
= 0; i
< width
; i
++, i4
+= 4) {
462 ASSERT(acc
[i4
+0] < max
);
463 ASSERT(acc
[i4
+1] < max
);
464 ASSERT(acc
[i4
+2] < max
);
465 ASSERT(acc
[i4
+3] < max
);
466 rgba
[i
][RCOMP
] = multTable
[acc
[i4
+0]];
467 rgba
[i
][GCOMP
] = multTable
[acc
[i4
+1]];
468 rgba
[i
][BCOMP
] = multTable
[acc
[i4
+2]];
469 rgba
[i
][ACOMP
] = multTable
[acc
[i4
+3]];
471 if (colorMask
!= 0xffffffff) {
472 _mesa_mask_rgba_span( ctx
, width
, xpos
, ypos
, rgba
);
474 (*ctx
->Driver
.WriteRGBASpan
)( ctx
, width
, xpos
, ypos
,
475 (const GLchan (*)[4])rgba
, NULL
);
476 if (ctx
->DrawBuffer
->UseSoftwareAlphaBuffers
477 && ctx
->Color
.ColorMask
[ACOMP
]) {
478 _mesa_write_alpha_span(ctx
, width
, xpos
, ypos
,
479 (CONST
GLubyte (*)[4]) rgba
, NULL
);
485 const GLfloat rscale
= value
/ acc_scale
* fChanMax
;
486 const GLfloat gscale
= value
/ acc_scale
* fChanMax
;
487 const GLfloat bscale
= value
/ acc_scale
* fChanMax
;
488 const GLfloat ascale
= value
/ acc_scale
* fChanMax
;
490 for (j
=0;j
<height
;j
++) {
491 const GLaccum
*acc
= ctx
->DrawBuffer
->Accum
+ ypos
* width4
+ xpos
*4;
492 for (i
=0;i
<width
;i
++) {
494 r
= (GLint
) ( (GLfloat
) (*acc
++) * rscale
+ 0.5F
);
495 g
= (GLint
) ( (GLfloat
) (*acc
++) * gscale
+ 0.5F
);
496 b
= (GLint
) ( (GLfloat
) (*acc
++) * bscale
+ 0.5F
);
497 a
= (GLint
) ( (GLfloat
) (*acc
++) * ascale
+ 0.5F
);
498 rgba
[i
][RCOMP
] = CLAMP( r
, 0, iChanMax
);
499 rgba
[i
][GCOMP
] = CLAMP( g
, 0, iChanMax
);
500 rgba
[i
][BCOMP
] = CLAMP( b
, 0, iChanMax
);
501 rgba
[i
][ACOMP
] = CLAMP( a
, 0, iChanMax
);
503 if (colorMask
!= 0xffffffff) {
504 _mesa_mask_rgba_span( ctx
, width
, xpos
, ypos
, rgba
);
506 (*ctx
->Driver
.WriteRGBASpan
)( ctx
, width
, xpos
, ypos
,
507 (const GLchan (*)[4])rgba
, NULL
);
508 if (ctx
->DrawBuffer
->UseSoftwareAlphaBuffers
509 && ctx
->Color
.ColorMask
[ACOMP
]) {
510 _mesa_write_alpha_span(ctx
, width
, xpos
, ypos
,
511 (CONST
GLubyte (*)[4]) rgba
, NULL
);
520 _mesa_error( ctx
, GL_INVALID_ENUM
, "glAccum" );