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"
32 #include "s_context.h"
33 #include "s_masking.h"
37 /* XXX this would have to change for accum buffers with more or less
38 * than 16 bits per color channel.
40 #define ACCUM_SCALE16 32767.0
44 * Accumulation buffer notes
46 * Normally, accumulation buffer values are GLshorts with values in
47 * [-32767, 32767] which represent floating point colors in [-1, 1],
48 * as defined by the OpenGL specification.
50 * We optimize for the common case used for full-scene antialiasing:
51 * // start with accum buffer cleared to zero
52 * glAccum(GL_LOAD, w); // or GL_ACCUM the first image
53 * glAccum(GL_ACCUM, w);
55 * glAccum(GL_ACCUM, w);
56 * glAccum(GL_RETURN, 1.0);
57 * That is, we start with an empty accumulation buffer and accumulate
58 * n images, each with weight w = 1/n.
59 * In this scenario, we can simply store unscaled integer values in
60 * the accum buffer instead of scaled integers. We'll also keep track
61 * of the w value so when we do GL_RETURN we simply divide the accumulated
62 * values by n (n=1/w).
63 * This lets us avoid _many_ int->float->int conversions.
68 /* enable the optimization */
69 #define USE_OPTIMIZED_ACCUM 1
71 #define USE_OPTIMIZED_ACCUM 0
76 * This is called when we fall out of optimized/unscaled accum buffer mode.
77 * That is, we convert each unscaled accum buffer value into a scaled value
78 * representing the range[-1, 1].
81 rescale_accum( GLcontext
*ctx
)
83 SWcontext
*swrast
= SWRAST_CONTEXT(ctx
);
84 struct gl_renderbuffer
*rb
85 = ctx
->DrawBuffer
->Attachment
[BUFFER_ACCUM
].Renderbuffer
;
86 const GLfloat s
= swrast
->_IntegerAccumScaler
* (32767.0F
/ CHAN_MAXF
);
89 assert(rb
->_BaseFormat
== GL_RGBA
);
90 /* add other types in future? */
91 assert(rb
->DataType
== GL_SHORT
|| rb
->DataType
== GL_UNSIGNED_SHORT
);
92 assert(swrast
->_IntegerAccumMode
);
94 if (rb
->GetPointer(ctx
, rb
, 0, 0)) {
95 /* directly-addressable memory */
97 for (y
= 0; y
< rb
->Height
; y
++) {
99 GLshort
*acc
= (GLshort
*) rb
->GetPointer(ctx
, rb
, 0, y
);
100 for (i
= 0; i
< 4 * rb
->Width
; i
++) {
101 acc
[i
] = (GLshort
) (acc
[i
] * s
);
106 /* use get/put row funcs */
108 for (y
= 0; y
< rb
->Height
; y
++) {
109 GLshort accRow
[MAX_WIDTH
* 4];
111 rb
->GetRow(ctx
, rb
, rb
->Width
, 0, y
, accRow
);
112 for (i
= 0; i
< 4 * rb
->Width
; i
++) {
113 accRow
[i
] = (GLshort
) (accRow
[i
] * s
);
115 rb
->PutRow(ctx
, rb
, rb
->Width
, 0, y
, accRow
, NULL
);
119 swrast
->_IntegerAccumMode
= GL_FALSE
;
125 * Clear the accumulation Buffer.
128 _swrast_clear_accum_buffer( GLcontext
*ctx
, struct gl_renderbuffer
*rb
)
130 SWcontext
*swrast
= SWRAST_CONTEXT(ctx
);
131 GLuint x
, y
, width
, height
;
133 if (ctx
->Visual
.accumRedBits
== 0) {
134 /* No accumulation buffer! Not an error. */
138 if (!rb
|| !rb
->Data
)
141 assert(rb
->_BaseFormat
== GL_RGBA
);
142 /* add other types in future? */
143 assert(rb
->DataType
== GL_SHORT
|| rb
->DataType
== GL_UNSIGNED_SHORT
);
145 /* bounds, with scissor */
146 x
= ctx
->DrawBuffer
->_Xmin
;
147 y
= ctx
->DrawBuffer
->_Ymin
;
148 width
= ctx
->DrawBuffer
->_Xmax
- ctx
->DrawBuffer
->_Xmin
;
149 height
= ctx
->DrawBuffer
->_Ymax
- ctx
->DrawBuffer
->_Ymin
;
151 if (rb
->DataType
== GL_SHORT
|| rb
->DataType
== GL_UNSIGNED_SHORT
) {
152 const GLfloat accScale
= 32767.0;
156 clearVal
[0] = (GLshort
) (ctx
->Accum
.ClearColor
[0] * accScale
);
157 clearVal
[1] = (GLshort
) (ctx
->Accum
.ClearColor
[1] * accScale
);
158 clearVal
[2] = (GLshort
) (ctx
->Accum
.ClearColor
[2] * accScale
);
159 clearVal
[3] = (GLshort
) (ctx
->Accum
.ClearColor
[3] * accScale
);
161 for (i
= 0; i
< height
; i
++) {
162 rb
->PutMonoRow(ctx
, rb
, width
, x
, y
+ i
, clearVal
, NULL
);
166 /* someday support other sizes */
169 /* update optimized accum state vars */
170 if (ctx
->Accum
.ClearColor
[0] == 0.0 && ctx
->Accum
.ClearColor
[1] == 0.0 &&
171 ctx
->Accum
.ClearColor
[2] == 0.0 && ctx
->Accum
.ClearColor
[3] == 0.0) {
172 #if USE_OPTIMIZED_ACCUM
173 swrast
->_IntegerAccumMode
= GL_TRUE
;
175 swrast
->_IntegerAccumMode
= GL_FALSE
;
177 swrast
->_IntegerAccumScaler
= 0.0; /* denotes empty accum buffer */
180 swrast
->_IntegerAccumMode
= GL_FALSE
;
186 accum_add(GLcontext
*ctx
, GLfloat value
,
187 GLint xpos
, GLint ypos
, GLint width
, GLint height
)
189 SWcontext
*swrast
= SWRAST_CONTEXT(ctx
);
190 struct gl_renderbuffer
*rb
191 = ctx
->DrawBuffer
->Attachment
[BUFFER_ACCUM
].Renderbuffer
;
195 /* Leave optimized accum buffer mode */
196 if (swrast
->_IntegerAccumMode
)
199 if (rb
->DataType
== GL_SHORT
|| rb
->DataType
== GL_UNSIGNED_SHORT
) {
200 const GLshort incr
= (GLshort
) (value
* ACCUM_SCALE16
);
201 if (rb
->GetPointer(ctx
, rb
, 0, 0)) {
203 for (i
= 0; i
< height
; i
++) {
204 GLshort
*acc
= (GLshort
*) rb
->GetPointer(ctx
, rb
, xpos
, ypos
+ i
);
205 for (j
= 0; j
< 4 * width
; j
++) {
212 for (i
= 0; i
< height
; i
++) {
213 GLshort accRow
[4 * MAX_WIDTH
];
214 rb
->GetRow(ctx
, rb
, width
, xpos
, ypos
+ i
, accRow
);
215 for (j
= 0; j
< 4 * width
; j
++) {
218 rb
->PutRow(ctx
, rb
, width
, xpos
, ypos
+ i
, accRow
, NULL
);
223 /* other types someday */
229 accum_mult(GLcontext
*ctx
, GLfloat mult
,
230 GLint xpos
, GLint ypos
, GLint width
, GLint height
)
232 SWcontext
*swrast
= SWRAST_CONTEXT(ctx
);
233 struct gl_renderbuffer
*rb
234 = ctx
->DrawBuffer
->Attachment
[BUFFER_ACCUM
].Renderbuffer
;
238 /* Leave optimized accum buffer mode */
239 if (swrast
->_IntegerAccumMode
)
242 if (rb
->DataType
== GL_SHORT
|| rb
->DataType
== GL_UNSIGNED_SHORT
) {
243 if (rb
->GetPointer(ctx
, rb
, 0, 0)) {
245 for (i
= 0; i
< height
; i
++) {
246 GLshort
*acc
= (GLshort
*) rb
->GetPointer(ctx
, rb
, xpos
, ypos
+ i
);
247 for (j
= 0; j
< 4 * width
; j
++) {
248 acc
[j
] = (GLshort
) (acc
[j
] * mult
);
254 for (i
= 0; i
< height
; i
++) {
255 GLshort accRow
[4 * MAX_WIDTH
];
256 rb
->GetRow(ctx
, rb
, width
, xpos
, ypos
+ i
, accRow
);
257 for (j
= 0; j
< 4 * width
; j
++) {
258 accRow
[j
] = (GLshort
) (accRow
[j
] * mult
);
260 rb
->PutRow(ctx
, rb
, width
, xpos
, ypos
+ i
, accRow
, NULL
);
265 /* other types someday */
272 accum_accum(GLcontext
*ctx
, GLfloat value
,
273 GLint xpos
, GLint ypos
, GLint width
, GLint height
)
275 SWcontext
*swrast
= SWRAST_CONTEXT(ctx
);
276 struct gl_renderbuffer
*rb
277 = ctx
->DrawBuffer
->Attachment
[BUFFER_ACCUM
].Renderbuffer
;
278 const GLboolean directAccess
= (rb
->GetPointer(ctx
, rb
, 0, 0) != NULL
);
282 if (!ctx
->ReadBuffer
->_ColorReadBuffer
) {
283 /* no read buffer - OK */
287 /* May have to leave optimized accum buffer mode */
288 if (swrast
->_IntegerAccumScaler
== 0.0 && value
> 0.0 && value
<= 1.0)
289 swrast
->_IntegerAccumScaler
= value
;
290 if (swrast
->_IntegerAccumMode
&& value
!= swrast
->_IntegerAccumScaler
)
293 if (rb
->DataType
== GL_SHORT
|| rb
->DataType
== GL_UNSIGNED_SHORT
) {
294 const GLfloat scale
= value
* ACCUM_SCALE16
/ CHAN_MAXF
;
295 GLshort accumRow
[4 * MAX_WIDTH
];
296 GLchan rgba
[MAX_WIDTH
][4];
299 for (i
= 0; i
< height
; i
++) {
302 acc
= (GLshort
*) rb
->GetPointer(ctx
, rb
, xpos
, ypos
+ i
);
305 rb
->GetRow(ctx
, rb
, width
, xpos
, ypos
+ i
, accumRow
);
309 /* read colors from color buffer */
310 _swrast_read_rgba_span(ctx
, ctx
->ReadBuffer
->_ColorReadBuffer
, width
,
311 xpos
, ypos
+ i
, CHAN_TYPE
, rgba
);
313 /* do accumulation */
314 if (swrast
->_IntegerAccumMode
) {
315 /* simply add integer color values into accum buffer */
317 for (j
= 0; j
< width
; j
++) {
318 acc
[j
* 4 + 0] += rgba
[j
][RCOMP
];
319 acc
[j
* 4 + 1] += rgba
[j
][GCOMP
];
320 acc
[j
* 4 + 2] += rgba
[j
][BCOMP
];
321 acc
[j
* 4 + 3] += rgba
[j
][ACOMP
];
325 /* scaled integer (or float) accum buffer */
327 for (j
= 0; j
< width
; j
++) {
328 acc
[j
* 4 + 0] += (GLshort
) ((GLfloat
) rgba
[j
][RCOMP
] * scale
);
329 acc
[j
* 4 + 1] += (GLshort
) ((GLfloat
) rgba
[j
][GCOMP
] * scale
);
330 acc
[j
* 4 + 2] += (GLshort
) ((GLfloat
) rgba
[j
][BCOMP
] * scale
);
331 acc
[j
* 4 + 3] += (GLshort
) ((GLfloat
) rgba
[j
][ACOMP
] * scale
);
336 rb
->PutRow(ctx
, rb
, width
, xpos
, ypos
+ i
, accumRow
, NULL
);
341 /* other types someday */
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 if (rb
->DataType
== GL_SHORT
|| rb
->DataType
== GL_UNSIGNED_SHORT
) {
378 const GLfloat scale
= value
* ACCUM_SCALE16
/ CHAN_MAXF
;
379 GLshort accumRow
[4 * MAX_WIDTH
];
380 GLchan rgba
[MAX_WIDTH
][4];
383 for (i
= 0; i
< height
; i
++) {
386 acc
= (GLshort
*) rb
->GetPointer(ctx
, rb
, xpos
, ypos
+ i
);
389 rb
->GetRow(ctx
, rb
, width
, xpos
, ypos
+ i
, accumRow
);
393 /* read colors from color buffer */
394 _swrast_read_rgba_span(ctx
, ctx
->ReadBuffer
->_ColorReadBuffer
, width
,
395 xpos
, ypos
+ i
, CHAN_TYPE
, rgba
);
398 if (swrast
->_IntegerAccumMode
) {
399 /* just copy values in */
401 assert(swrast
->_IntegerAccumScaler
> 0.0);
402 assert(swrast
->_IntegerAccumScaler
<= 1.0);
403 for (j
= 0; j
< width
; j
++) {
404 acc
[j
* 4 + 0] = rgba
[j
][RCOMP
];
405 acc
[j
* 4 + 1] = rgba
[j
][GCOMP
];
406 acc
[j
* 4 + 2] = rgba
[j
][BCOMP
];
407 acc
[j
* 4 + 3] = rgba
[j
][ACOMP
];
411 /* scaled integer (or float) accum buffer */
413 for (j
= 0; j
< width
; j
++) {
414 acc
[j
* 4 + 0] = (GLshort
) ((GLfloat
) rgba
[j
][RCOMP
] * scale
);
415 acc
[j
* 4 + 1] = (GLshort
) ((GLfloat
) rgba
[j
][GCOMP
] * scale
);
416 acc
[j
* 4 + 2] = (GLshort
) ((GLfloat
) rgba
[j
][BCOMP
] * scale
);
417 acc
[j
* 4 + 3] = (GLshort
) ((GLfloat
) rgba
[j
][ACOMP
] * scale
);
422 rb
->PutRow(ctx
, rb
, width
, xpos
, ypos
+ i
, accumRow
, NULL
);
430 accum_return(GLcontext
*ctx
, GLfloat value
,
431 GLint xpos
, GLint ypos
, GLint width
, GLint height
)
433 SWcontext
*swrast
= SWRAST_CONTEXT(ctx
);
434 struct gl_framebuffer
*fb
= ctx
->DrawBuffer
;
435 struct gl_renderbuffer
*accumRb
= fb
->Attachment
[BUFFER_ACCUM
].Renderbuffer
;
436 const GLboolean directAccess
437 = (accumRb
->GetPointer(ctx
, accumRb
, 0, 0) != NULL
);
439 static GLchan multTable
[32768];
440 static GLfloat prevMult
= 0.0;
441 const GLfloat mult
= swrast
->_IntegerAccumScaler
;
442 const GLint max
= MIN2((GLint
) (256 / mult
), 32767);
444 /* May have to leave optimized accum buffer mode */
445 if (swrast
->_IntegerAccumMode
&& value
!= 1.0)
448 if (swrast
->_IntegerAccumMode
&& swrast
->_IntegerAccumScaler
> 0) {
449 /* build lookup table to avoid many floating point multiplies */
451 assert(swrast
->_IntegerAccumScaler
<= 1.0);
452 if (mult
!= prevMult
) {
453 for (j
= 0; j
< max
; j
++)
454 multTable
[j
] = IROUND((GLfloat
) j
* mult
);
459 if (accumRb
->DataType
== GL_SHORT
||
460 accumRb
->DataType
== GL_UNSIGNED_SHORT
) {
461 const GLfloat scale
= value
* CHAN_MAXF
/ ACCUM_SCALE16
;
465 /* XXX maybe transpose the 'i' and 'buffer' loops??? */
466 for (i
= 0; i
< height
; i
++) {
467 GLshort accumRow
[4 * MAX_WIDTH
];
471 /* init color span */
472 INIT_SPAN(span
, GL_BITMAP
);
474 span
.arrayMask
= SPAN_RGBA
;
479 acc
= (GLshort
*) accumRb
->GetPointer(ctx
, accumRb
, xpos
, ypos
+i
);
482 accumRb
->GetRow(ctx
, accumRb
, width
, xpos
, ypos
+ i
, accumRow
);
486 /* get the colors to return */
487 if (swrast
->_IntegerAccumMode
) {
489 for (j
= 0; j
< width
; j
++) {
490 ASSERT(acc
[j
* 4 + 0] < max
);
491 ASSERT(acc
[j
* 4 + 1] < max
);
492 ASSERT(acc
[j
* 4 + 2] < max
);
493 ASSERT(acc
[j
* 4 + 3] < max
);
494 span
.array
->rgba
[j
][RCOMP
] = multTable
[acc
[j
* 4 + 0]];
495 span
.array
->rgba
[j
][GCOMP
] = multTable
[acc
[j
* 4 + 1]];
496 span
.array
->rgba
[j
][BCOMP
] = multTable
[acc
[j
* 4 + 2]];
497 span
.array
->rgba
[j
][ACOMP
] = multTable
[acc
[j
* 4 + 3]];
501 /* scaled integer (or float) accum buffer */
503 for (j
= 0; j
< width
; j
++) {
505 GLchan r
= acc
[j
* 4 + 0] * scale
;
506 GLchan g
= acc
[j
* 4 + 1] * scale
;
507 GLchan b
= acc
[j
* 4 + 2] * scale
;
508 GLchan a
= acc
[j
* 4 + 3] * scale
;
510 GLint r
= IROUND( (GLfloat
) (acc
[j
* 4 + 0]) * scale
);
511 GLint g
= IROUND( (GLfloat
) (acc
[j
* 4 + 1]) * scale
);
512 GLint b
= IROUND( (GLfloat
) (acc
[j
* 4 + 2]) * scale
);
513 GLint a
= IROUND( (GLfloat
) (acc
[j
* 4 + 3]) * scale
);
515 span
.array
->rgba
[j
][RCOMP
] = CLAMP( r
, 0, CHAN_MAX
);
516 span
.array
->rgba
[j
][GCOMP
] = CLAMP( g
, 0, CHAN_MAX
);
517 span
.array
->rgba
[j
][BCOMP
] = CLAMP( b
, 0, CHAN_MAX
);
518 span
.array
->rgba
[j
][ACOMP
] = CLAMP( a
, 0, CHAN_MAX
);
523 for (buffer
= 0; buffer
< fb
->_NumColorDrawBuffers
; buffer
++) {
524 struct gl_renderbuffer
*rb
= fb
->_ColorDrawBuffers
[buffer
];
525 const GLboolean masking
= (!ctx
->Color
.ColorMask
[buffer
][RCOMP
] ||
526 !ctx
->Color
.ColorMask
[buffer
][GCOMP
] ||
527 !ctx
->Color
.ColorMask
[buffer
][BCOMP
] ||
528 !ctx
->Color
.ColorMask
[buffer
][ACOMP
]);
530 _swrast_mask_rgba_span(ctx
, rb
, &span
, buffer
);
532 rb
->PutRow(ctx
, rb
, width
, xpos
, ypos
+ i
, span
.array
->rgba
, NULL
);
537 /* other types someday */
544 * Software fallback for glAccum.
547 _swrast_Accum(GLcontext
*ctx
, GLenum op
, GLfloat value
)
549 SWcontext
*swrast
= SWRAST_CONTEXT(ctx
);
550 GLint xpos
, ypos
, width
, height
;
552 if (swrast
->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 swrast_render_start(ctx
);
562 /* Compute region after calling swrast_render_start() so that we know the
563 * drawbuffer's size/bounds are up to date.
565 xpos
= ctx
->DrawBuffer
->_Xmin
;
566 ypos
= ctx
->DrawBuffer
->_Ymin
;
567 width
= ctx
->DrawBuffer
->_Xmax
- ctx
->DrawBuffer
->_Xmin
;
568 height
= ctx
->DrawBuffer
->_Ymax
- ctx
->DrawBuffer
->_Ymin
;
573 accum_add(ctx
, value
, xpos
, ypos
, width
, height
);
578 accum_mult(ctx
, value
, xpos
, ypos
, width
, height
);
583 accum_accum(ctx
, value
, xpos
, ypos
, width
, height
);
587 accum_load(ctx
, value
, xpos
, ypos
, width
, height
);
590 accum_return(ctx
, value
, xpos
, ypos
, width
, height
);
593 _mesa_problem(ctx
, "invalid mode in _swrast_Accum()");
597 swrast_render_finish(ctx
);