2 * Mesa 3-D graphics library
5 * Copyright (C) 1999-2006 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.
26 #include "main/glheader.h"
27 #include "main/context.h"
28 #include "main/macros.h"
29 #include "main/imports.h"
30 #include "main/fbobject.h"
33 #include "s_context.h"
34 #include "s_masking.h"
38 /* XXX this would have to change for accum buffers with more or less
39 * than 16 bits per color channel.
41 #define ACCUM_SCALE16 32767.0
45 * Accumulation buffer notes
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.
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);
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.
69 /* enable the optimization */
70 #define USE_OPTIMIZED_ACCUM 1
72 #define USE_OPTIMIZED_ACCUM 0
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].
82 rescale_accum( GLcontext
*ctx
)
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
);
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
);
95 if (rb
->GetPointer(ctx
, rb
, 0, 0)) {
96 /* directly-addressable memory */
98 for (y
= 0; y
< rb
->Height
; y
++) {
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
);
107 /* use get/put row funcs */
109 for (y
= 0; y
< rb
->Height
; y
++) {
110 GLshort accRow
[MAX_WIDTH
* 4];
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
);
116 rb
->PutRow(ctx
, rb
, rb
->Width
, 0, y
, accRow
, NULL
);
120 swrast
->_IntegerAccumMode
= GL_FALSE
;
126 * Clear the accumulation Buffer.
129 _swrast_clear_accum_buffer( GLcontext
*ctx
, struct gl_renderbuffer
*rb
)
131 SWcontext
*swrast
= SWRAST_CONTEXT(ctx
);
132 GLuint x
, y
, width
, height
;
134 if (ctx
->Visual
.accumRedBits
== 0) {
135 /* No accumulation buffer! Not an error. */
139 if (!rb
|| !rb
->Data
)
142 assert(rb
->_BaseFormat
== GL_RGBA
);
143 /* add other types in future? */
144 assert(rb
->DataType
== GL_SHORT
|| rb
->DataType
== GL_UNSIGNED_SHORT
);
146 /* bounds, with scissor */
147 x
= ctx
->DrawBuffer
->_Xmin
;
148 y
= ctx
->DrawBuffer
->_Ymin
;
149 width
= ctx
->DrawBuffer
->_Xmax
- ctx
->DrawBuffer
->_Xmin
;
150 height
= ctx
->DrawBuffer
->_Ymax
- ctx
->DrawBuffer
->_Ymin
;
152 if (rb
->DataType
== GL_SHORT
|| rb
->DataType
== GL_UNSIGNED_SHORT
) {
153 const GLfloat accScale
= 32767.0;
157 clearVal
[0] = (GLshort
) (ctx
->Accum
.ClearColor
[0] * accScale
);
158 clearVal
[1] = (GLshort
) (ctx
->Accum
.ClearColor
[1] * accScale
);
159 clearVal
[2] = (GLshort
) (ctx
->Accum
.ClearColor
[2] * accScale
);
160 clearVal
[3] = (GLshort
) (ctx
->Accum
.ClearColor
[3] * accScale
);
162 for (i
= 0; i
< height
; i
++) {
163 rb
->PutMonoRow(ctx
, rb
, width
, x
, y
+ i
, clearVal
, NULL
);
167 /* someday support other sizes */
170 /* update optimized accum state vars */
171 if (ctx
->Accum
.ClearColor
[0] == 0.0 && ctx
->Accum
.ClearColor
[1] == 0.0 &&
172 ctx
->Accum
.ClearColor
[2] == 0.0 && ctx
->Accum
.ClearColor
[3] == 0.0) {
173 #if USE_OPTIMIZED_ACCUM
174 swrast
->_IntegerAccumMode
= GL_TRUE
;
176 swrast
->_IntegerAccumMode
= GL_FALSE
;
178 swrast
->_IntegerAccumScaler
= 0.0; /* denotes empty accum buffer */
181 swrast
->_IntegerAccumMode
= GL_FALSE
;
187 accum_add(GLcontext
*ctx
, GLfloat value
,
188 GLint xpos
, GLint ypos
, GLint width
, GLint height
)
190 SWcontext
*swrast
= SWRAST_CONTEXT(ctx
);
191 struct gl_renderbuffer
*rb
192 = ctx
->DrawBuffer
->Attachment
[BUFFER_ACCUM
].Renderbuffer
;
196 /* Leave optimized accum buffer mode */
197 if (swrast
->_IntegerAccumMode
)
200 if (rb
->DataType
== GL_SHORT
|| rb
->DataType
== GL_UNSIGNED_SHORT
) {
201 const GLshort incr
= (GLshort
) (value
* ACCUM_SCALE16
);
202 if (rb
->GetPointer(ctx
, rb
, 0, 0)) {
204 for (i
= 0; i
< height
; i
++) {
205 GLshort
*acc
= (GLshort
*) rb
->GetPointer(ctx
, rb
, xpos
, ypos
+ i
);
206 for (j
= 0; j
< 4 * width
; j
++) {
213 for (i
= 0; i
< height
; i
++) {
214 GLshort accRow
[4 * MAX_WIDTH
];
215 rb
->GetRow(ctx
, rb
, width
, xpos
, ypos
+ i
, accRow
);
216 for (j
= 0; j
< 4 * width
; j
++) {
219 rb
->PutRow(ctx
, rb
, width
, xpos
, ypos
+ i
, accRow
, NULL
);
224 /* other types someday */
230 accum_mult(GLcontext
*ctx
, GLfloat mult
,
231 GLint xpos
, GLint ypos
, GLint width
, GLint height
)
233 SWcontext
*swrast
= SWRAST_CONTEXT(ctx
);
234 struct gl_renderbuffer
*rb
235 = ctx
->DrawBuffer
->Attachment
[BUFFER_ACCUM
].Renderbuffer
;
239 /* Leave optimized accum buffer mode */
240 if (swrast
->_IntegerAccumMode
)
243 if (rb
->DataType
== GL_SHORT
|| rb
->DataType
== GL_UNSIGNED_SHORT
) {
244 if (rb
->GetPointer(ctx
, rb
, 0, 0)) {
246 for (i
= 0; i
< height
; i
++) {
247 GLshort
*acc
= (GLshort
*) rb
->GetPointer(ctx
, rb
, xpos
, ypos
+ i
);
248 for (j
= 0; j
< 4 * width
; j
++) {
249 acc
[j
] = (GLshort
) (acc
[j
] * mult
);
255 for (i
= 0; i
< height
; i
++) {
256 GLshort accRow
[4 * MAX_WIDTH
];
257 rb
->GetRow(ctx
, rb
, width
, xpos
, ypos
+ i
, accRow
);
258 for (j
= 0; j
< 4 * width
; j
++) {
259 accRow
[j
] = (GLshort
) (accRow
[j
] * mult
);
261 rb
->PutRow(ctx
, rb
, width
, xpos
, ypos
+ i
, accRow
, NULL
);
266 /* other types someday */
273 accum_accum(GLcontext
*ctx
, GLfloat value
,
274 GLint xpos
, GLint ypos
, GLint width
, GLint height
)
276 SWcontext
*swrast
= SWRAST_CONTEXT(ctx
);
277 struct gl_renderbuffer
*rb
278 = ctx
->DrawBuffer
->Attachment
[BUFFER_ACCUM
].Renderbuffer
;
279 const GLboolean directAccess
= (rb
->GetPointer(ctx
, rb
, 0, 0) != NULL
);
283 if (!ctx
->ReadBuffer
->_ColorReadBuffer
) {
284 /* no read buffer - OK */
288 /* May have to leave optimized accum buffer mode */
289 if (swrast
->_IntegerAccumScaler
== 0.0 && value
> 0.0 && value
<= 1.0)
290 swrast
->_IntegerAccumScaler
= value
;
291 if (swrast
->_IntegerAccumMode
&& value
!= swrast
->_IntegerAccumScaler
)
294 if (rb
->DataType
== GL_SHORT
|| rb
->DataType
== GL_UNSIGNED_SHORT
) {
295 const GLfloat scale
= value
* ACCUM_SCALE16
/ CHAN_MAXF
;
296 GLshort accumRow
[4 * MAX_WIDTH
];
297 GLchan rgba
[MAX_WIDTH
][4];
300 for (i
= 0; i
< height
; i
++) {
303 acc
= (GLshort
*) rb
->GetPointer(ctx
, rb
, xpos
, ypos
+ i
);
306 rb
->GetRow(ctx
, rb
, width
, xpos
, ypos
+ i
, accumRow
);
310 /* read colors from color buffer */
311 _swrast_read_rgba_span(ctx
, ctx
->ReadBuffer
->_ColorReadBuffer
, width
,
312 xpos
, ypos
+ i
, CHAN_TYPE
, rgba
);
314 /* do accumulation */
315 if (swrast
->_IntegerAccumMode
) {
316 /* simply add integer color values into accum buffer */
318 for (j
= 0; j
< width
; j
++) {
319 acc
[j
* 4 + 0] += rgba
[j
][RCOMP
];
320 acc
[j
* 4 + 1] += rgba
[j
][GCOMP
];
321 acc
[j
* 4 + 2] += rgba
[j
][BCOMP
];
322 acc
[j
* 4 + 3] += rgba
[j
][ACOMP
];
326 /* scaled integer (or float) accum buffer */
328 for (j
= 0; j
< width
; j
++) {
329 acc
[j
* 4 + 0] += (GLshort
) ((GLfloat
) rgba
[j
][RCOMP
] * scale
);
330 acc
[j
* 4 + 1] += (GLshort
) ((GLfloat
) rgba
[j
][GCOMP
] * scale
);
331 acc
[j
* 4 + 2] += (GLshort
) ((GLfloat
) rgba
[j
][BCOMP
] * scale
);
332 acc
[j
* 4 + 3] += (GLshort
) ((GLfloat
) rgba
[j
][ACOMP
] * scale
);
337 rb
->PutRow(ctx
, rb
, width
, xpos
, ypos
+ i
, accumRow
, NULL
);
342 /* other types someday */
349 accum_load(GLcontext
*ctx
, GLfloat value
,
350 GLint xpos
, GLint ypos
, GLint width
, GLint height
)
352 SWcontext
*swrast
= SWRAST_CONTEXT(ctx
);
353 struct gl_renderbuffer
*rb
354 = ctx
->DrawBuffer
->Attachment
[BUFFER_ACCUM
].Renderbuffer
;
355 const GLboolean directAccess
= (rb
->GetPointer(ctx
, rb
, 0, 0) != NULL
);
359 if (!ctx
->ReadBuffer
->_ColorReadBuffer
) {
360 /* no read buffer - OK */
364 /* This is a change to go into optimized accum buffer mode */
365 if (value
> 0.0 && value
<= 1.0) {
366 #if USE_OPTIMIZED_ACCUM
367 swrast
->_IntegerAccumMode
= GL_TRUE
;
369 swrast
->_IntegerAccumMode
= GL_FALSE
;
371 swrast
->_IntegerAccumScaler
= value
;
374 swrast
->_IntegerAccumMode
= GL_FALSE
;
375 swrast
->_IntegerAccumScaler
= 0.0;
378 if (rb
->DataType
== GL_SHORT
|| rb
->DataType
== GL_UNSIGNED_SHORT
) {
379 const GLfloat scale
= value
* ACCUM_SCALE16
/ CHAN_MAXF
;
380 GLshort accumRow
[4 * MAX_WIDTH
];
381 GLchan rgba
[MAX_WIDTH
][4];
384 for (i
= 0; i
< height
; i
++) {
387 acc
= (GLshort
*) rb
->GetPointer(ctx
, rb
, xpos
, ypos
+ i
);
390 rb
->GetRow(ctx
, rb
, width
, xpos
, ypos
+ i
, accumRow
);
394 /* read colors from color buffer */
395 _swrast_read_rgba_span(ctx
, ctx
->ReadBuffer
->_ColorReadBuffer
, width
,
396 xpos
, ypos
+ i
, CHAN_TYPE
, rgba
);
399 if (swrast
->_IntegerAccumMode
) {
400 /* just copy values in */
402 assert(swrast
->_IntegerAccumScaler
> 0.0);
403 assert(swrast
->_IntegerAccumScaler
<= 1.0);
404 for (j
= 0; j
< width
; j
++) {
405 acc
[j
* 4 + 0] = rgba
[j
][RCOMP
];
406 acc
[j
* 4 + 1] = rgba
[j
][GCOMP
];
407 acc
[j
* 4 + 2] = rgba
[j
][BCOMP
];
408 acc
[j
* 4 + 3] = rgba
[j
][ACOMP
];
412 /* scaled integer (or float) accum buffer */
414 for (j
= 0; j
< width
; j
++) {
415 acc
[j
* 4 + 0] = (GLshort
) ((GLfloat
) rgba
[j
][RCOMP
] * scale
);
416 acc
[j
* 4 + 1] = (GLshort
) ((GLfloat
) rgba
[j
][GCOMP
] * scale
);
417 acc
[j
* 4 + 2] = (GLshort
) ((GLfloat
) rgba
[j
][BCOMP
] * scale
);
418 acc
[j
* 4 + 3] = (GLshort
) ((GLfloat
) rgba
[j
][ACOMP
] * scale
);
423 rb
->PutRow(ctx
, rb
, width
, xpos
, ypos
+ i
, accumRow
, NULL
);
431 accum_return(GLcontext
*ctx
, GLfloat value
,
432 GLint xpos
, GLint ypos
, GLint width
, GLint height
)
434 SWcontext
*swrast
= SWRAST_CONTEXT(ctx
);
435 struct gl_framebuffer
*fb
= ctx
->DrawBuffer
;
436 struct gl_renderbuffer
*accumRb
= fb
->Attachment
[BUFFER_ACCUM
].Renderbuffer
;
437 const GLboolean directAccess
438 = (accumRb
->GetPointer(ctx
, accumRb
, 0, 0) != NULL
);
439 const GLboolean masking
= (!ctx
->Color
.ColorMask
[RCOMP
] ||
440 !ctx
->Color
.ColorMask
[GCOMP
] ||
441 !ctx
->Color
.ColorMask
[BCOMP
] ||
442 !ctx
->Color
.ColorMask
[ACOMP
]);
444 static GLchan multTable
[32768];
445 static GLfloat prevMult
= 0.0;
446 const GLfloat mult
= swrast
->_IntegerAccumScaler
;
447 const GLint max
= MIN2((GLint
) (256 / mult
), 32767);
449 /* May have to leave optimized accum buffer mode */
450 if (swrast
->_IntegerAccumMode
&& value
!= 1.0)
453 if (swrast
->_IntegerAccumMode
&& swrast
->_IntegerAccumScaler
> 0) {
454 /* build lookup table to avoid many floating point multiplies */
456 assert(swrast
->_IntegerAccumScaler
<= 1.0);
457 if (mult
!= prevMult
) {
458 for (j
= 0; j
< max
; j
++)
459 multTable
[j
] = IROUND((GLfloat
) j
* mult
);
464 if (accumRb
->DataType
== GL_SHORT
||
465 accumRb
->DataType
== GL_UNSIGNED_SHORT
) {
466 const GLfloat scale
= value
* CHAN_MAXF
/ ACCUM_SCALE16
;
470 /* XXX maybe transpose the 'i' and 'buffer' loops??? */
471 for (i
= 0; i
< height
; i
++) {
472 GLshort accumRow
[4 * MAX_WIDTH
];
476 /* init color span */
477 INIT_SPAN(span
, GL_BITMAP
);
479 span
.arrayMask
= SPAN_RGBA
;
484 acc
= (GLshort
*) accumRb
->GetPointer(ctx
, accumRb
, xpos
, ypos
+i
);
487 accumRb
->GetRow(ctx
, accumRb
, width
, xpos
, ypos
+ i
, accumRow
);
491 /* get the colors to return */
492 if (swrast
->_IntegerAccumMode
) {
494 for (j
= 0; j
< width
; j
++) {
495 ASSERT(acc
[j
* 4 + 0] < max
);
496 ASSERT(acc
[j
* 4 + 1] < max
);
497 ASSERT(acc
[j
* 4 + 2] < max
);
498 ASSERT(acc
[j
* 4 + 3] < max
);
499 span
.array
->rgba
[j
][RCOMP
] = multTable
[acc
[j
* 4 + 0]];
500 span
.array
->rgba
[j
][GCOMP
] = multTable
[acc
[j
* 4 + 1]];
501 span
.array
->rgba
[j
][BCOMP
] = multTable
[acc
[j
* 4 + 2]];
502 span
.array
->rgba
[j
][ACOMP
] = multTable
[acc
[j
* 4 + 3]];
506 /* scaled integer (or float) accum buffer */
508 for (j
= 0; j
< width
; j
++) {
510 GLchan r
= acc
[j
* 4 + 0] * scale
;
511 GLchan g
= acc
[j
* 4 + 1] * scale
;
512 GLchan b
= acc
[j
* 4 + 2] * scale
;
513 GLchan a
= acc
[j
* 4 + 3] * scale
;
515 GLint r
= IROUND( (GLfloat
) (acc
[j
* 4 + 0]) * scale
);
516 GLint g
= IROUND( (GLfloat
) (acc
[j
* 4 + 1]) * scale
);
517 GLint b
= IROUND( (GLfloat
) (acc
[j
* 4 + 2]) * scale
);
518 GLint a
= IROUND( (GLfloat
) (acc
[j
* 4 + 3]) * scale
);
520 span
.array
->rgba
[j
][RCOMP
] = CLAMP( r
, 0, CHAN_MAX
);
521 span
.array
->rgba
[j
][GCOMP
] = CLAMP( g
, 0, CHAN_MAX
);
522 span
.array
->rgba
[j
][BCOMP
] = CLAMP( b
, 0, CHAN_MAX
);
523 span
.array
->rgba
[j
][ACOMP
] = CLAMP( a
, 0, CHAN_MAX
);
528 for (buffer
= 0; buffer
< fb
->_NumColorDrawBuffers
; buffer
++) {
529 struct gl_renderbuffer
*rb
= fb
->_ColorDrawBuffers
[buffer
];
531 _swrast_mask_rgba_span(ctx
, rb
, &span
);
533 rb
->PutRow(ctx
, rb
, width
, xpos
, ypos
+ i
, span
.array
->rgba
, NULL
);
538 /* other types someday */
545 * Software fallback for glAccum.
548 _swrast_Accum(GLcontext
*ctx
, GLenum op
, GLfloat value
)
550 SWcontext
*swrast
= SWRAST_CONTEXT(ctx
);
551 GLint xpos
, ypos
, width
, height
;
553 if (swrast
->NewState
)
554 _swrast_validate_derived( ctx
);
556 if (!ctx
->DrawBuffer
->Attachment
[BUFFER_ACCUM
].Renderbuffer
) {
557 _mesa_warning(ctx
, "Calling glAccum() without an accumulation buffer");
561 swrast_render_start(ctx
);
563 /* Compute region after calling swrast_render_start() so that we know the
564 * drawbuffer's size/bounds are up to date.
566 xpos
= ctx
->DrawBuffer
->_Xmin
;
567 ypos
= ctx
->DrawBuffer
->_Ymin
;
568 width
= ctx
->DrawBuffer
->_Xmax
- ctx
->DrawBuffer
->_Xmin
;
569 height
= ctx
->DrawBuffer
->_Ymax
- ctx
->DrawBuffer
->_Ymin
;
574 accum_add(ctx
, value
, xpos
, ypos
, width
, height
);
579 accum_mult(ctx
, value
, xpos
, ypos
, width
, height
);
584 accum_accum(ctx
, value
, xpos
, ypos
, width
, height
);
588 accum_load(ctx
, value
, xpos
, ypos
, width
, height
);
591 accum_return(ctx
, value
, xpos
, ypos
, width
, height
);
594 _mesa_problem(ctx
, "invalid mode in _swrast_Accum()");
598 swrast_render_finish(ctx
);