ff12c5305032259a2b2c3ad8200070272e2d148b
1 /* $Id: s_accum.c,v 1.2 2000/11/05 18:24:40 keithw Exp $ */
4 * Mesa 3-D graphics library
7 * Copyright (C) 1999-2000 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_context.h"
34 #include "s_masking.h"
39 * Accumulation buffer notes
41 * Normally, accumulation buffer values are GLshorts with values in
42 * [-32767, 32767] which represent floating point colors in [-1, 1],
43 * as suggested by the OpenGL specification.
45 * We optimize for the common case used for full-scene antialiasing:
46 * // start with accum buffer cleared to zero
47 * glAccum(GL_LOAD, w); // or GL_ACCUM the first image
48 * glAccum(GL_ACCUM, w);
50 * glAccum(GL_ACCUM, w);
51 * glAccum(GL_RETURN, 1.0);
52 * That is, we start with an empty accumulation buffer and accumulate
53 * n images, each with weight w = 1/n.
54 * In this scenario, we can simply store unscaled integer values in
55 * the accum buffer instead of scaled integers. We'll also keep track
56 * of the w value so when we do GL_RETURN we simply divide the accumulated
58 * This lets us avoid _many_ int->float->int conversions.
63 #define USE_OPTIMIZED_ACCUM /* enable the optimization */
69 _mesa_alloc_accum_buffer( GLcontext
*ctx
)
73 if (ctx
->DrawBuffer
->Accum
) {
74 FREE( ctx
->DrawBuffer
->Accum
);
75 ctx
->DrawBuffer
->Accum
= NULL
;
78 /* allocate accumulation buffer if not already present */
79 n
= ctx
->DrawBuffer
->Width
* ctx
->DrawBuffer
->Height
* 4 * sizeof(GLaccum
);
80 ctx
->DrawBuffer
->Accum
= (GLaccum
*) MALLOC( n
);
81 if (!ctx
->DrawBuffer
->Accum
) {
82 /* unable to setup accumulation buffer */
83 gl_error( ctx
, GL_OUT_OF_MEMORY
, "glAccum" );
85 #ifdef USE_OPTIMIZED_ACCUM
86 ctx
->IntegerAccumMode
= GL_TRUE
;
88 ctx
->IntegerAccumMode
= GL_FALSE
;
90 ctx
->IntegerAccumScaler
= 0.0;
99 * This is called when we fall out of optimized/unscaled accum buffer mode.
100 * That is, we convert each unscaled accum buffer value into a scaled value
101 * representing the range[-1, 1].
103 static void rescale_accum( GLcontext
*ctx
)
105 const GLuint n
= ctx
->DrawBuffer
->Width
* ctx
->DrawBuffer
->Height
* 4;
106 const GLfloat fChanMax
= (1 << (sizeof(GLchan
) * 8)) - 1;
107 const GLfloat s
= ctx
->IntegerAccumScaler
* (32767.0 / fChanMax
);
108 GLaccum
*accum
= ctx
->DrawBuffer
->Accum
;
111 assert(ctx
->IntegerAccumMode
);
114 for (i
= 0; i
< n
; i
++) {
115 accum
[i
] = (GLaccum
) (accum
[i
] * s
);
118 ctx
->IntegerAccumMode
= GL_FALSE
;
127 * Clear the accumulation Buffer.
130 _mesa_clear_accum_buffer( GLcontext
*ctx
)
135 if (ctx
->Visual
.AccumRedBits
==0) {
136 /* No accumulation buffer! */
140 if (sizeof(GLaccum
)==1) {
143 else if (sizeof(GLaccum
)==2) {
147 /* sizeof(GLaccum) > 2 (Cray) */
148 acc_scale
= (float) SHRT_MAX
;
151 /* number of pixels */
152 buffersize
= ctx
->DrawBuffer
->Width
* ctx
->DrawBuffer
->Height
;
154 if (!ctx
->DrawBuffer
->Accum
) {
155 /* try to alloc accumulation buffer */
156 ctx
->DrawBuffer
->Accum
= (GLaccum
*)
157 MALLOC( buffersize
* 4 * sizeof(GLaccum
) );
160 if (ctx
->DrawBuffer
->Accum
) {
161 if (ctx
->Scissor
.Enabled
) {
162 /* Limit clear to scissor box */
167 r
= (GLaccum
) (ctx
->Accum
.ClearColor
[0] * acc_scale
);
168 g
= (GLaccum
) (ctx
->Accum
.ClearColor
[1] * acc_scale
);
169 b
= (GLaccum
) (ctx
->Accum
.ClearColor
[2] * acc_scale
);
170 a
= (GLaccum
) (ctx
->Accum
.ClearColor
[3] * acc_scale
);
171 /* size of region to clear */
172 width
= 4 * (ctx
->DrawBuffer
->Xmax
- ctx
->DrawBuffer
->Xmin
);
173 height
= ctx
->DrawBuffer
->Ymax
- ctx
->DrawBuffer
->Ymin
;
174 /* ptr to first element to clear */
175 row
= ctx
->DrawBuffer
->Accum
176 + 4 * (ctx
->DrawBuffer
->Ymin
* ctx
->DrawBuffer
->Width
177 + ctx
->DrawBuffer
->Xmin
);
178 for (j
=0;j
<height
;j
++) {
179 for (i
=0;i
<width
;i
+=4) {
185 row
+= 4 * ctx
->DrawBuffer
->Width
;
189 /* clear whole buffer */
190 if (ctx
->Accum
.ClearColor
[0]==0.0 &&
191 ctx
->Accum
.ClearColor
[1]==0.0 &&
192 ctx
->Accum
.ClearColor
[2]==0.0 &&
193 ctx
->Accum
.ClearColor
[3]==0.0) {
195 BZERO( ctx
->DrawBuffer
->Accum
, buffersize
* 4 * sizeof(GLaccum
) );
199 GLaccum
*acc
, r
, g
, b
, a
;
202 acc
= ctx
->DrawBuffer
->Accum
;
203 r
= (GLaccum
) (ctx
->Accum
.ClearColor
[0] * acc_scale
);
204 g
= (GLaccum
) (ctx
->Accum
.ClearColor
[1] * acc_scale
);
205 b
= (GLaccum
) (ctx
->Accum
.ClearColor
[2] * acc_scale
);
206 a
= (GLaccum
) (ctx
->Accum
.ClearColor
[3] * acc_scale
);
207 for (i
=0;i
<buffersize
;i
++) {
216 /* update optimized accum state vars */
217 if (ctx
->Accum
.ClearColor
[0] == 0.0 && ctx
->Accum
.ClearColor
[1] == 0.0 &&
218 ctx
->Accum
.ClearColor
[2] == 0.0 && ctx
->Accum
.ClearColor
[3] == 0.0) {
219 #ifdef USE_OPTIMIZED_ACCUM
220 ctx
->IntegerAccumMode
= GL_TRUE
;
222 ctx
->IntegerAccumMode
= GL_FALSE
;
224 ctx
->IntegerAccumScaler
= 0.0; /* denotes empty accum buffer */
227 ctx
->IntegerAccumMode
= GL_FALSE
;
234 _swrast_Accum( GLcontext
*ctx
, GLenum op
, GLfloat value
,
235 GLint xpos
, GLint ypos
,
236 GLint width
, GLint height
)
241 GLchan rgba
[MAX_WIDTH
][4];
242 const GLuint colorMask
= *((GLuint
*) &ctx
->Color
.ColorMask
);
243 const GLint iChanMax
= (1 << (sizeof(GLchan
) * 8)) - 1;
244 const GLfloat fChanMax
= (1 << (sizeof(GLchan
) * 8)) - 1;
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) {
264 /* sizeof(GLaccum) > 2 (Cray) */
265 acc_scale
= (float) SHRT_MAX
;
273 const GLaccum intVal
= (GLaccum
) (value
* acc_scale
);
275 /* Leave optimized accum buffer mode */
276 if (ctx
->IntegerAccumMode
)
278 for (j
= 0; j
< height
; j
++) {
279 GLaccum
* acc
= ctx
->DrawBuffer
->Accum
+ ypos
* width4
+ 4 * xpos
;
281 for (i
= 0; i
< width4
; i
++) {
292 /* Leave optimized accum buffer mode */
293 if (ctx
->IntegerAccumMode
)
295 for (j
= 0; j
< height
; j
++) {
296 GLaccum
*acc
= ctx
->DrawBuffer
->Accum
+ ypos
* width4
+ 4 * xpos
;
298 for (i
= 0; i
< width4
; i
++) {
299 acc
[i
] = (GLaccum
) ( (GLfloat
) acc
[i
] * value
);
310 (*ctx
->Driver
.SetReadBuffer
)( ctx
, ctx
->ReadBuffer
,
311 ctx
->Pixel
.DriverReadBuffer
);
313 /* May have to leave optimized accum buffer mode */
314 if (ctx
->IntegerAccumScaler
== 0.0 && value
> 0.0 && value
<= 1.0)
315 ctx
->IntegerAccumScaler
= value
;
316 if (ctx
->IntegerAccumMode
&& value
!= ctx
->IntegerAccumScaler
)
321 if (ctx
->IntegerAccumMode
) {
322 /* simply add integer color values into accum buffer */
324 GLaccum
*acc
= ctx
->DrawBuffer
->Accum
+ ypos
* width4
+ xpos
* 4;
325 assert(ctx
->IntegerAccumScaler
> 0.0);
326 assert(ctx
->IntegerAccumScaler
<= 1.0);
327 for (j
= 0; j
< height
; j
++) {
330 gl_read_rgba_span(ctx
, ctx
->DrawBuffer
, width
, xpos
, ypos
, rgba
);
331 for (i
= i4
= 0; i
< width
; i
++, i4
+=4) {
332 acc
[i4
+0] += rgba
[i
][RCOMP
];
333 acc
[i4
+1] += rgba
[i
][GCOMP
];
334 acc
[i4
+2] += rgba
[i
][BCOMP
];
335 acc
[i4
+3] += rgba
[i
][ACOMP
];
342 /* scaled integer accum buffer */
343 const GLfloat rscale
= value
* acc_scale
/ fChanMax
;
344 const GLfloat gscale
= value
* acc_scale
/ fChanMax
;
345 const GLfloat bscale
= value
* acc_scale
/ fChanMax
;
346 const GLfloat ascale
= value
* acc_scale
/ fChanMax
;
348 for (j
=0;j
<height
;j
++) {
349 GLaccum
*acc
= ctx
->DrawBuffer
->Accum
+ ypos
* width4
+ xpos
* 4;
351 gl_read_rgba_span(ctx
, ctx
->DrawBuffer
, width
, xpos
, ypos
, rgba
);
352 for (i
=0;i
<width
;i
++) {
353 *acc
+= (GLaccum
) ( (GLfloat
) rgba
[i
][RCOMP
] * rscale
); acc
++;
354 *acc
+= (GLaccum
) ( (GLfloat
) rgba
[i
][GCOMP
] * gscale
); acc
++;
355 *acc
+= (GLaccum
) ( (GLfloat
) rgba
[i
][BCOMP
] * bscale
); acc
++;
356 *acc
+= (GLaccum
) ( (GLfloat
) rgba
[i
][ACOMP
] * ascale
); acc
++;
361 /* restore read buffer = draw buffer (the default) */
362 (*ctx
->Driver
.SetReadBuffer
)( ctx
, ctx
->DrawBuffer
,
363 ctx
->Color
.DriverDrawBuffer
);
368 (*ctx
->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 ctx
->IntegerAccumMode
= GL_TRUE
;
376 ctx
->IntegerAccumMode
= GL_FALSE
;
378 ctx
->IntegerAccumScaler
= value
;
381 ctx
->IntegerAccumMode
= GL_FALSE
;
382 ctx
->IntegerAccumScaler
= 0.0;
386 if (ctx
->IntegerAccumMode
) {
387 /* just copy values into accum buffer */
389 GLaccum
*acc
= ctx
->DrawBuffer
->Accum
+ ypos
* width4
+ xpos
* 4;
390 assert(ctx
->IntegerAccumScaler
> 0.0);
391 assert(ctx
->IntegerAccumScaler
<= 1.0);
392 for (j
= 0; j
< height
; j
++) {
394 gl_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 accum buffer */
407 const GLfloat rscale
= value
* acc_scale
/ fChanMax
;
408 const GLfloat gscale
= value
* acc_scale
/ fChanMax
;
409 const GLfloat bscale
= value
* acc_scale
/ fChanMax
;
410 const GLfloat ascale
= value
* acc_scale
/ fChanMax
;
411 const GLfloat d
= 3.0 / acc_scale
;
413 for (j
= 0; j
< height
; j
++) {
414 GLaccum
*acc
= ctx
->DrawBuffer
->Accum
+ ypos
* width4
+ xpos
* 4;
415 gl_read_rgba_span(ctx
, ctx
->DrawBuffer
, width
, xpos
, ypos
, rgba
);
416 for (i
=0;i
<width
;i
++) {
417 *acc
++ = (GLaccum
) ((GLfloat
) rgba
[i
][RCOMP
] * rscale
+ d
);
418 *acc
++ = (GLaccum
) ((GLfloat
) rgba
[i
][GCOMP
] * gscale
+ d
);
419 *acc
++ = (GLaccum
) ((GLfloat
) rgba
[i
][BCOMP
] * bscale
+ d
);
420 *acc
++ = (GLaccum
) ((GLfloat
) rgba
[i
][ACOMP
] * ascale
+ d
);
426 /* restore read buffer = draw buffer (the default) */
427 (*ctx
->Driver
.SetReadBuffer
)( ctx
, ctx
->DrawBuffer
,
428 ctx
->Color
.DriverDrawBuffer
);
433 /* May have to leave optimized accum buffer mode */
434 if (ctx
->IntegerAccumMode
&& value
!= 1.0)
438 if (ctx
->IntegerAccumMode
&& ctx
->IntegerAccumScaler
> 0) {
439 /* build lookup table to avoid many floating point multiplies */
440 static GLchan multTable
[32768];
441 static GLfloat prevMult
= 0.0;
442 const GLfloat mult
= ctx
->IntegerAccumScaler
;
443 const GLint max
= MIN2((GLint
) (256 / mult
), 32767);
445 if (mult
!= prevMult
) {
446 for (j
= 0; j
< max
; j
++)
447 multTable
[j
] = (GLint
) ((GLfloat
) j
* mult
+ 0.5F
);
451 assert(ctx
->IntegerAccumScaler
> 0.0);
452 assert(ctx
->IntegerAccumScaler
<= 1.0);
453 for (j
= 0; j
< height
; j
++) {
454 const GLaccum
*acc
= ctx
->DrawBuffer
->Accum
+ ypos
* width4
+ xpos
*4;
456 for (i
= i4
= 0; i
< width
; i
++, i4
+= 4) {
457 ASSERT(acc
[i4
+0] < max
);
458 ASSERT(acc
[i4
+1] < max
);
459 ASSERT(acc
[i4
+2] < max
);
460 ASSERT(acc
[i4
+3] < max
);
461 rgba
[i
][RCOMP
] = multTable
[acc
[i4
+0]];
462 rgba
[i
][GCOMP
] = multTable
[acc
[i4
+1]];
463 rgba
[i
][BCOMP
] = multTable
[acc
[i4
+2]];
464 rgba
[i
][ACOMP
] = multTable
[acc
[i4
+3]];
466 if (colorMask
!= 0xffffffff) {
467 _mesa_mask_rgba_span( ctx
, width
, xpos
, ypos
, rgba
);
469 (*ctx
->Driver
.WriteRGBASpan
)( ctx
, width
, xpos
, ypos
,
470 (const GLchan (*)[4])rgba
, NULL
);
475 const GLfloat rscale
= value
/ acc_scale
* fChanMax
;
476 const GLfloat gscale
= value
/ acc_scale
* fChanMax
;
477 const GLfloat bscale
= value
/ acc_scale
* fChanMax
;
478 const GLfloat ascale
= value
/ acc_scale
* fChanMax
;
480 for (j
=0;j
<height
;j
++) {
481 const GLaccum
*acc
= ctx
->DrawBuffer
->Accum
+ ypos
* width4
+ xpos
*4;
482 for (i
=0;i
<width
;i
++) {
484 r
= (GLint
) ( (GLfloat
) (*acc
++) * rscale
+ 0.5F
);
485 g
= (GLint
) ( (GLfloat
) (*acc
++) * gscale
+ 0.5F
);
486 b
= (GLint
) ( (GLfloat
) (*acc
++) * bscale
+ 0.5F
);
487 a
= (GLint
) ( (GLfloat
) (*acc
++) * ascale
+ 0.5F
);
488 rgba
[i
][RCOMP
] = CLAMP( r
, 0, iChanMax
);
489 rgba
[i
][GCOMP
] = CLAMP( g
, 0, iChanMax
);
490 rgba
[i
][BCOMP
] = CLAMP( b
, 0, iChanMax
);
491 rgba
[i
][ACOMP
] = CLAMP( a
, 0, iChanMax
);
493 if (colorMask
!= 0xffffffff) {
494 _mesa_mask_rgba_span( ctx
, width
, xpos
, ypos
, rgba
);
496 (*ctx
->Driver
.WriteRGBASpan
)( ctx
, width
, xpos
, ypos
,
497 (const GLchan (*)[4])rgba
, NULL
);
505 gl_error( ctx
, GL_INVALID_ENUM
, "glAccum" );