2 * Mesa 3-D graphics library
5 * Copyright (C) 1999-2005 Brian Paul All Rights Reserved.
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:
14 * The above copyright notice and this permission notice shall be included
15 * in all copies or substantial portions of the Software.
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.
33 #include "s_context.h"
34 #include "s_masking.h"
38 #define ACCUM_SCALE16 32767.0
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 defined 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
60 * values by n (n=1/w).
61 * This lets us avoid _many_ int->float->int conversions.
65 #if CHAN_BITS == 8 && ACCUM_BITS <= 32
66 /* enable the optimization */
67 #define USE_OPTIMIZED_ACCUM 1
69 #define USE_OPTIMIZED_ACCUM 0
74 * This is called when we fall out of optimized/unscaled accum buffer mode.
75 * That is, we convert each unscaled accum buffer value into a scaled value
76 * representing the range[-1, 1].
79 rescale_accum( GLcontext
*ctx
)
81 SWcontext
*swrast
= SWRAST_CONTEXT(ctx
);
82 struct gl_renderbuffer
*rb
83 = ctx
->DrawBuffer
->Attachment
[BUFFER_ACCUM
].Renderbuffer
;
84 const GLfloat s
= swrast
->_IntegerAccumScaler
* (32767.0F
/ CHAN_MAXF
);
87 assert(rb
->_BaseFormat
== GL_RGBA
);
88 /* add other types in future? */
89 assert(rb
->DataType
== GL_SHORT
|| rb
->DataType
== GL_UNSIGNED_SHORT
);
90 assert(swrast
->_IntegerAccumMode
);
92 if (rb
->GetPointer(ctx
, rb
, 0, 0)) {
93 /* directly-addressable memory */
95 for (y
= 0; y
< rb
->Height
; y
++) {
97 GLshort
*acc
= (GLshort
*) rb
->GetPointer(ctx
, rb
, 0, y
);
98 for (i
= 0; i
< 4 * rb
->Width
; i
++) {
99 acc
[i
] = (GLshort
) (acc
[i
] * s
);
104 /* use get/put row funcs */
106 for (y
= 0; y
< rb
->Height
; y
++) {
107 GLshort accRow
[MAX_WIDTH
* 4];
109 rb
->GetRow(ctx
, rb
, rb
->Width
, 0, y
, accRow
);
110 for (i
= 0; i
< 4 * rb
->Width
; i
++) {
111 accRow
[i
] = (GLshort
) (accRow
[i
] * s
);
113 rb
->PutRow(ctx
, rb
, rb
->Width
, 0, y
, accRow
, NULL
);
117 swrast
->_IntegerAccumMode
= GL_FALSE
;
123 * Clear the accumulation Buffer.
126 _swrast_clear_accum_buffer( GLcontext
*ctx
, struct gl_renderbuffer
*rb
)
128 SWcontext
*swrast
= SWRAST_CONTEXT(ctx
);
129 GLuint x
, y
, width
, height
;
131 if (ctx
->Visual
.accumRedBits
== 0) {
132 /* No accumulation buffer! Not an error. */
137 assert(rb
->_BaseFormat
== GL_RGBA
);
138 /* add other types in future? */
139 assert(rb
->DataType
== GL_SHORT
|| rb
->DataType
== GL_UNSIGNED_SHORT
);
141 /* bounds, with scissor */
142 x
= ctx
->DrawBuffer
->_Xmin
;
143 y
= ctx
->DrawBuffer
->_Ymin
;
144 width
= ctx
->DrawBuffer
->_Xmax
- ctx
->DrawBuffer
->_Xmin
;
145 height
= ctx
->DrawBuffer
->_Ymax
- ctx
->DrawBuffer
->_Ymin
;
147 if (rb
->DataType
== GL_SHORT
|| rb
->DataType
== GL_UNSIGNED_SHORT
) {
148 const GLfloat accScale
= 32767.0;
152 clearVal
[0] = (GLshort
) (ctx
->Accum
.ClearColor
[0] * accScale
);
153 clearVal
[1] = (GLshort
) (ctx
->Accum
.ClearColor
[1] * accScale
);
154 clearVal
[2] = (GLshort
) (ctx
->Accum
.ClearColor
[2] * accScale
);
155 clearVal
[3] = (GLshort
) (ctx
->Accum
.ClearColor
[3] * accScale
);
157 for (i
= 0; i
< height
; i
++) {
158 rb
->PutMonoRow(ctx
, rb
, width
, x
, y
+ i
, clearVal
, NULL
);
162 /* someday support other sizes */
165 /* update optimized accum state vars */
166 if (ctx
->Accum
.ClearColor
[0] == 0.0 && ctx
->Accum
.ClearColor
[1] == 0.0 &&
167 ctx
->Accum
.ClearColor
[2] == 0.0 && ctx
->Accum
.ClearColor
[3] == 0.0) {
168 #if USE_OPTIMIZED_ACCUM
169 swrast
->_IntegerAccumMode
= GL_TRUE
;
171 swrast
->_IntegerAccumMode
= GL_FALSE
;
173 swrast
->_IntegerAccumScaler
= 0.0; /* denotes empty accum buffer */
176 swrast
->_IntegerAccumMode
= GL_FALSE
;
182 accum_add(GLcontext
*ctx
, GLfloat value
,
183 GLint xpos
, GLint ypos
, GLint width
, GLint height
)
185 SWcontext
*swrast
= SWRAST_CONTEXT(ctx
);
186 struct gl_renderbuffer
*rb
187 = ctx
->DrawBuffer
->Attachment
[BUFFER_ACCUM
].Renderbuffer
;
191 /* Leave optimized accum buffer mode */
192 if (swrast
->_IntegerAccumMode
)
195 if (rb
->DataType
== GL_SHORT
|| rb
->DataType
== GL_UNSIGNED_SHORT
) {
196 const GLshort incr
= (GLshort
) (value
* ACCUM_SCALE16
);
197 if (rb
->GetPointer(ctx
, rb
, 0, 0)) {
199 for (i
= 0; i
< height
; i
++) {
200 GLshort
*acc
= (GLshort
*) rb
->GetPointer(ctx
, rb
, xpos
, ypos
+ i
);
201 for (j
= 0; j
< 4 * width
; j
++) {
208 for (i
= 0; i
< height
; i
++) {
209 GLshort accRow
[4 * MAX_WIDTH
];
210 rb
->GetRow(ctx
, rb
, width
, xpos
, ypos
+ i
, accRow
);
211 for (j
= 0; j
< 4 * width
; j
++) {
214 rb
->PutRow(ctx
, rb
, width
, xpos
, ypos
+ i
, accRow
, NULL
);
219 /* other types someday */
225 accum_mult(GLcontext
*ctx
, GLfloat mult
,
226 GLint xpos
, GLint ypos
, GLint width
, GLint height
)
228 SWcontext
*swrast
= SWRAST_CONTEXT(ctx
);
229 struct gl_renderbuffer
*rb
230 = ctx
->DrawBuffer
->Attachment
[BUFFER_ACCUM
].Renderbuffer
;
234 /* Leave optimized accum buffer mode */
235 if (swrast
->_IntegerAccumMode
)
238 if (rb
->DataType
== GL_SHORT
|| rb
->DataType
== GL_UNSIGNED_SHORT
) {
239 if (rb
->GetPointer(ctx
, rb
, 0, 0)) {
241 for (i
= 0; i
< height
; i
++) {
242 GLshort
*acc
= (GLshort
*) rb
->GetPointer(ctx
, rb
, xpos
, ypos
+ i
);
243 for (j
= 0; j
< 4 * width
; j
++) {
244 acc
[j
] = (GLshort
) (acc
[j
] * mult
);
250 for (i
= 0; i
< height
; i
++) {
251 GLshort accRow
[4 * MAX_WIDTH
];
252 rb
->GetRow(ctx
, rb
, width
, xpos
, ypos
+ i
, accRow
);
253 for (j
= 0; j
< 4 * width
; j
++) {
254 accRow
[j
] = (GLshort
) (accRow
[j
] * mult
);
256 rb
->PutRow(ctx
, rb
, width
, xpos
, ypos
+ i
, accRow
, NULL
);
261 /* other types someday */
268 accum_accum(GLcontext
*ctx
, GLfloat value
,
269 GLint xpos
, GLint ypos
, GLint width
, GLint height
)
271 SWcontext
*swrast
= SWRAST_CONTEXT(ctx
);
272 struct gl_renderbuffer
*rb
273 = ctx
->DrawBuffer
->Attachment
[BUFFER_ACCUM
].Renderbuffer
;
274 const GLboolean directAccess
= (rb
->GetPointer(ctx
, rb
, 0, 0) != NULL
);
278 if (!ctx
->ReadBuffer
->_ColorReadBuffer
) {
279 /* no read buffer - OK */
283 /* May have to leave optimized accum buffer mode */
284 if (swrast
->_IntegerAccumScaler
== 0.0 && value
> 0.0 && value
<= 1.0)
285 swrast
->_IntegerAccumScaler
= value
;
286 if (swrast
->_IntegerAccumMode
&& value
!= swrast
->_IntegerAccumScaler
)
289 _swrast_use_read_buffer(ctx
);
291 if (rb
->DataType
== GL_SHORT
|| rb
->DataType
== GL_UNSIGNED_SHORT
) {
292 const GLfloat scale
= value
* ACCUM_SCALE16
/ CHAN_MAXF
;
293 GLshort accumRow
[4 * MAX_WIDTH
];
294 GLchan rgba
[MAX_WIDTH
][4];
297 for (i
= 0; i
< height
; i
++) {
300 acc
= (GLshort
*) rb
->GetPointer(ctx
, rb
, xpos
, ypos
+ i
);
303 rb
->GetRow(ctx
, rb
, width
, xpos
, ypos
+ i
, accumRow
);
307 /* read colors from color buffer */
308 _swrast_read_rgba_span(ctx
, ctx
->ReadBuffer
->_ColorReadBuffer
, width
,
309 xpos
, ypos
+ i
, rgba
);
311 /* do accumulation */
312 if (swrast
->_IntegerAccumMode
) {
313 /* simply add integer color values into accum buffer */
315 for (j
= 0; j
< width
; j
++) {
316 acc
[j
* 4 + 0] += rgba
[j
][RCOMP
];
317 acc
[j
* 4 + 1] += rgba
[j
][GCOMP
];
318 acc
[j
* 4 + 2] += rgba
[j
][BCOMP
];
319 acc
[j
* 4 + 3] += rgba
[j
][ACOMP
];
323 /* scaled integer (or float) accum buffer */
325 for (j
= 0; j
< width
; j
++) {
326 acc
[j
* 4 + 0] += (GLshort
) ((GLfloat
) rgba
[j
][RCOMP
] * scale
);
327 acc
[j
* 4 + 1] += (GLshort
) ((GLfloat
) rgba
[j
][GCOMP
] * scale
);
328 acc
[j
* 4 + 2] += (GLshort
) ((GLfloat
) rgba
[j
][BCOMP
] * scale
);
329 acc
[j
* 4 + 3] += (GLshort
) ((GLfloat
) rgba
[j
][ACOMP
] * scale
);
334 rb
->PutRow(ctx
, rb
, width
, xpos
, ypos
+ i
, accumRow
, NULL
);
339 /* other types someday */
342 _swrast_use_draw_buffer(ctx
);
348 accum_load(GLcontext
*ctx
, GLfloat value
,
349 GLint xpos
, GLint ypos
, GLint width
, GLint height
)
351 SWcontext
*swrast
= SWRAST_CONTEXT(ctx
);
352 struct gl_renderbuffer
*rb
353 = ctx
->DrawBuffer
->Attachment
[BUFFER_ACCUM
].Renderbuffer
;
354 const GLboolean directAccess
= (rb
->GetPointer(ctx
, rb
, 0, 0) != NULL
);
358 if (!ctx
->ReadBuffer
->_ColorReadBuffer
) {
359 /* no read buffer - OK */
363 /* This is a change to go into optimized accum buffer mode */
364 if (value
> 0.0 && value
<= 1.0) {
365 #if USE_OPTIMIZED_ACCUM
366 swrast
->_IntegerAccumMode
= GL_TRUE
;
368 swrast
->_IntegerAccumMode
= GL_FALSE
;
370 swrast
->_IntegerAccumScaler
= value
;
373 swrast
->_IntegerAccumMode
= GL_FALSE
;
374 swrast
->_IntegerAccumScaler
= 0.0;
377 _swrast_use_read_buffer(ctx
);
379 if (rb
->DataType
== GL_SHORT
|| rb
->DataType
== GL_UNSIGNED_SHORT
) {
380 const GLfloat scale
= value
* ACCUM_SCALE16
/ CHAN_MAXF
;
381 GLshort accumRow
[4 * MAX_WIDTH
];
382 GLchan rgba
[MAX_WIDTH
][4];
385 for (i
= 0; i
< height
; i
++) {
388 acc
= (GLshort
*) rb
->GetPointer(ctx
, rb
, xpos
, ypos
+ i
);
391 rb
->GetRow(ctx
, rb
, width
, xpos
, ypos
+ i
, accumRow
);
395 /* read colors from color buffer */
396 _swrast_read_rgba_span(ctx
, ctx
->ReadBuffer
->_ColorReadBuffer
, width
,
397 xpos
, ypos
+ i
, rgba
);
400 if (swrast
->_IntegerAccumMode
) {
401 /* just copy values in */
403 assert(swrast
->_IntegerAccumScaler
> 0.0);
404 assert(swrast
->_IntegerAccumScaler
<= 1.0);
405 for (j
= 0; j
< width
; j
++) {
406 acc
[j
* 4 + 0] = rgba
[j
][RCOMP
];
407 acc
[j
* 4 + 1] = rgba
[j
][GCOMP
];
408 acc
[j
* 4 + 2] = rgba
[j
][BCOMP
];
409 acc
[j
* 4 + 3] = rgba
[j
][ACOMP
];
413 /* scaled integer (or float) accum buffer */
415 for (j
= 0; j
< width
; j
++) {
416 acc
[j
* 4 + 0] = (GLshort
) ((GLfloat
) rgba
[j
][RCOMP
] * scale
);
417 acc
[j
* 4 + 1] = (GLshort
) ((GLfloat
) rgba
[j
][GCOMP
] * scale
);
418 acc
[j
* 4 + 2] = (GLshort
) ((GLfloat
) rgba
[j
][BCOMP
] * scale
);
419 acc
[j
* 4 + 3] = (GLshort
) ((GLfloat
) rgba
[j
][ACOMP
] * scale
);
424 rb
->PutRow(ctx
, rb
, width
, xpos
, ypos
+ i
, accumRow
, NULL
);
429 /* other types someday */
432 _swrast_use_draw_buffer(ctx
);
437 accum_return(GLcontext
*ctx
, GLfloat value
,
438 GLint xpos
, GLint ypos
, GLint width
, GLint height
)
440 SWcontext
*swrast
= SWRAST_CONTEXT(ctx
);
441 struct gl_framebuffer
*fb
= ctx
->DrawBuffer
;
442 struct gl_renderbuffer
*accumRb
= fb
->Attachment
[BUFFER_ACCUM
].Renderbuffer
;
443 const GLboolean directAccess
444 = (accumRb
->GetPointer(ctx
, accumRb
, 0, 0) != NULL
);
445 const GLboolean masking
= (!ctx
->Color
.ColorMask
[RCOMP
] ||
446 !ctx
->Color
.ColorMask
[GCOMP
] ||
447 !ctx
->Color
.ColorMask
[BCOMP
] ||
448 !ctx
->Color
.ColorMask
[ACOMP
]);
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 /* May have to leave optimized accum buffer mode */
456 if (swrast
->_IntegerAccumMode
&& value
!= 1.0)
459 if (swrast
->_IntegerAccumMode
&& swrast
->_IntegerAccumScaler
> 0) {
460 /* build lookup table to avoid many floating point multiplies */
462 assert(swrast
->_IntegerAccumScaler
<= 1.0);
463 if (mult
!= prevMult
) {
464 for (j
= 0; j
< max
; j
++)
465 multTable
[j
] = IROUND((GLfloat
) j
* mult
);
470 if (accumRb
->DataType
== GL_SHORT
||
471 accumRb
->DataType
== GL_UNSIGNED_SHORT
) {
472 const GLfloat scale
= value
* CHAN_MAXF
/ ACCUM_SCALE16
;
475 /* XXX maybe transpose the 'i' and 'buffer' loops??? */
476 for (i
= 0; i
< height
; i
++) {
477 GLchan rgba
[MAX_WIDTH
][4];
478 GLshort accumRow
[4 * MAX_WIDTH
];
482 acc
= (GLshort
*) accumRb
->GetPointer(ctx
, accumRb
, xpos
, ypos
+i
);
485 accumRb
->GetRow(ctx
, accumRb
, width
, xpos
, ypos
+ i
, accumRow
);
489 /* get the colors to return */
490 if (swrast
->_IntegerAccumMode
) {
492 for (j
= 0; j
< width
; j
++) {
493 ASSERT(acc
[j
* 4 + 0] < max
);
494 ASSERT(acc
[j
* 4 + 1] < max
);
495 ASSERT(acc
[j
* 4 + 2] < max
);
496 ASSERT(acc
[j
* 4 + 3] < max
);
497 rgba
[j
][RCOMP
] = multTable
[acc
[j
* 4 + 0]];
498 rgba
[j
][GCOMP
] = multTable
[acc
[j
* 4 + 1]];
499 rgba
[j
][BCOMP
] = multTable
[acc
[j
* 4 + 2]];
500 rgba
[j
][ACOMP
] = multTable
[acc
[j
* 4 + 3]];
504 /* scaled integer (or float) accum buffer */
506 for (j
= 0; j
< width
; j
++) {
507 GLint r
= IROUND( (GLfloat
) (acc
[j
* 4 + 0]) * scale
);
508 GLint g
= IROUND( (GLfloat
) (acc
[j
* 4 + 1]) * scale
);
509 GLint b
= IROUND( (GLfloat
) (acc
[j
* 4 + 2]) * scale
);
510 GLint a
= IROUND( (GLfloat
) (acc
[j
* 4 + 3]) * scale
);
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
);
519 for (buffer
= 0; buffer
< fb
->_NumColorDrawBuffers
[0]; buffer
++) {
520 struct gl_renderbuffer
*rb
= fb
->_ColorDrawBuffers
[0][buffer
];
522 _swrast_mask_rgba_array(ctx
, rb
, width
, xpos
, ypos
+ i
, rgba
);
525 if (swrast
->Driver
.WriteRGBASpan
)
526 swrast
->Driver
.WriteRGBASpan(ctx
, rb
, width
, xpos
, ypos
+ i
,
527 (const GLchan (*)[4]) rgba
, NULL
);
530 rb
->PutRow(ctx
, rb
, width
, xpos
, ypos
+ i
, rgba
, NULL
);
535 /* other types someday */
542 * Software fallback for glAccum.
545 _swrast_Accum( GLcontext
*ctx
, GLenum op
, GLfloat value
,
546 GLint xpos
, GLint ypos
,
547 GLint width
, GLint height
)
550 SWcontext
*swrast
= SWRAST_CONTEXT(ctx
);
552 if (SWRAST_CONTEXT(ctx
)->NewState
)
553 _swrast_validate_derived( ctx
);
555 if (!ctx
->DrawBuffer
->Attachment
[BUFFER_ACCUM
].Renderbuffer
) {
556 _mesa_warning(ctx
, "Calling glAccum() without an accumulation buffer");
560 RENDER_START(swrast
, ctx
);
565 accum_add(ctx
, value
, xpos
, ypos
, width
, height
);
570 accum_mult(ctx
, value
, xpos
, ypos
, width
, height
);
575 accum_accum(ctx
, value
, xpos
, ypos
, width
, height
);
579 accum_load(ctx
, value
, xpos
, ypos
, width
, height
);
582 accum_return(ctx
, value
, xpos
, ypos
, width
, height
);
585 _mesa_problem(ctx
, "invalid mode in _swrast_Accum()");
589 RENDER_FINISH(swrast
, ctx
);