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.
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. */
140 assert(rb
->_BaseFormat
== GL_RGBA
);
141 /* add other types in future? */
142 assert(rb
->DataType
== GL_SHORT
|| rb
->DataType
== GL_UNSIGNED_SHORT
);
144 /* bounds, with scissor */
145 x
= ctx
->DrawBuffer
->_Xmin
;
146 y
= ctx
->DrawBuffer
->_Ymin
;
147 width
= ctx
->DrawBuffer
->_Xmax
- ctx
->DrawBuffer
->_Xmin
;
148 height
= ctx
->DrawBuffer
->_Ymax
- ctx
->DrawBuffer
->_Ymin
;
150 if (rb
->DataType
== GL_SHORT
|| rb
->DataType
== GL_UNSIGNED_SHORT
) {
151 const GLfloat accScale
= 32767.0;
155 clearVal
[0] = (GLshort
) (ctx
->Accum
.ClearColor
[0] * accScale
);
156 clearVal
[1] = (GLshort
) (ctx
->Accum
.ClearColor
[1] * accScale
);
157 clearVal
[2] = (GLshort
) (ctx
->Accum
.ClearColor
[2] * accScale
);
158 clearVal
[3] = (GLshort
) (ctx
->Accum
.ClearColor
[3] * accScale
);
160 for (i
= 0; i
< height
; i
++) {
161 rb
->PutMonoRow(ctx
, rb
, width
, x
, y
+ i
, clearVal
, NULL
);
165 /* someday support other sizes */
168 /* update optimized accum state vars */
169 if (ctx
->Accum
.ClearColor
[0] == 0.0 && ctx
->Accum
.ClearColor
[1] == 0.0 &&
170 ctx
->Accum
.ClearColor
[2] == 0.0 && ctx
->Accum
.ClearColor
[3] == 0.0) {
171 #if USE_OPTIMIZED_ACCUM
172 swrast
->_IntegerAccumMode
= GL_TRUE
;
174 swrast
->_IntegerAccumMode
= GL_FALSE
;
176 swrast
->_IntegerAccumScaler
= 0.0; /* denotes empty accum buffer */
179 swrast
->_IntegerAccumMode
= GL_FALSE
;
185 accum_add(GLcontext
*ctx
, GLfloat value
,
186 GLint xpos
, GLint ypos
, GLint width
, GLint height
)
188 SWcontext
*swrast
= SWRAST_CONTEXT(ctx
);
189 struct gl_renderbuffer
*rb
190 = ctx
->DrawBuffer
->Attachment
[BUFFER_ACCUM
].Renderbuffer
;
194 /* Leave optimized accum buffer mode */
195 if (swrast
->_IntegerAccumMode
)
198 if (rb
->DataType
== GL_SHORT
|| rb
->DataType
== GL_UNSIGNED_SHORT
) {
199 const GLshort incr
= (GLshort
) (value
* ACCUM_SCALE16
);
200 if (rb
->GetPointer(ctx
, rb
, 0, 0)) {
202 for (i
= 0; i
< height
; i
++) {
203 GLshort
*acc
= (GLshort
*) rb
->GetPointer(ctx
, rb
, xpos
, ypos
+ i
);
204 for (j
= 0; j
< 4 * width
; j
++) {
211 for (i
= 0; i
< height
; i
++) {
212 GLshort accRow
[4 * MAX_WIDTH
];
213 rb
->GetRow(ctx
, rb
, width
, xpos
, ypos
+ i
, accRow
);
214 for (j
= 0; j
< 4 * width
; j
++) {
217 rb
->PutRow(ctx
, rb
, width
, xpos
, ypos
+ i
, accRow
, NULL
);
222 /* other types someday */
228 accum_mult(GLcontext
*ctx
, GLfloat mult
,
229 GLint xpos
, GLint ypos
, GLint width
, GLint height
)
231 SWcontext
*swrast
= SWRAST_CONTEXT(ctx
);
232 struct gl_renderbuffer
*rb
233 = ctx
->DrawBuffer
->Attachment
[BUFFER_ACCUM
].Renderbuffer
;
237 /* Leave optimized accum buffer mode */
238 if (swrast
->_IntegerAccumMode
)
241 if (rb
->DataType
== GL_SHORT
|| rb
->DataType
== GL_UNSIGNED_SHORT
) {
242 if (rb
->GetPointer(ctx
, rb
, 0, 0)) {
244 for (i
= 0; i
< height
; i
++) {
245 GLshort
*acc
= (GLshort
*) rb
->GetPointer(ctx
, rb
, xpos
, ypos
+ i
);
246 for (j
= 0; j
< 4 * width
; j
++) {
247 acc
[j
] = (GLshort
) (acc
[j
] * mult
);
253 for (i
= 0; i
< height
; i
++) {
254 GLshort accRow
[4 * MAX_WIDTH
];
255 rb
->GetRow(ctx
, rb
, width
, xpos
, ypos
+ i
, accRow
);
256 for (j
= 0; j
< 4 * width
; j
++) {
257 accRow
[j
] = (GLshort
) (accRow
[j
] * mult
);
259 rb
->PutRow(ctx
, rb
, width
, xpos
, ypos
+ i
, accRow
, NULL
);
264 /* other types someday */
271 accum_accum(GLcontext
*ctx
, GLfloat value
,
272 GLint xpos
, GLint ypos
, GLint width
, GLint height
)
274 SWcontext
*swrast
= SWRAST_CONTEXT(ctx
);
275 struct gl_renderbuffer
*rb
276 = ctx
->DrawBuffer
->Attachment
[BUFFER_ACCUM
].Renderbuffer
;
277 const GLboolean directAccess
= (rb
->GetPointer(ctx
, rb
, 0, 0) != NULL
);
281 if (!ctx
->ReadBuffer
->_ColorReadBuffer
) {
282 /* no read buffer - OK */
286 /* May have to leave optimized accum buffer mode */
287 if (swrast
->_IntegerAccumScaler
== 0.0 && value
> 0.0 && value
<= 1.0)
288 swrast
->_IntegerAccumScaler
= value
;
289 if (swrast
->_IntegerAccumMode
&& value
!= swrast
->_IntegerAccumScaler
)
292 if (rb
->DataType
== GL_SHORT
|| rb
->DataType
== GL_UNSIGNED_SHORT
) {
293 const GLfloat scale
= value
* ACCUM_SCALE16
/ CHAN_MAXF
;
294 GLshort accumRow
[4 * MAX_WIDTH
];
295 GLchan rgba
[MAX_WIDTH
][4];
298 for (i
= 0; i
< height
; i
++) {
301 acc
= (GLshort
*) rb
->GetPointer(ctx
, rb
, xpos
, ypos
+ i
);
304 rb
->GetRow(ctx
, rb
, width
, xpos
, ypos
+ i
, accumRow
);
308 /* read colors from color buffer */
309 _swrast_read_rgba_span(ctx
, ctx
->ReadBuffer
->_ColorReadBuffer
, width
,
310 xpos
, ypos
+ i
, rgba
);
312 /* do accumulation */
313 if (swrast
->_IntegerAccumMode
) {
314 /* simply add integer color values into accum buffer */
316 for (j
= 0; j
< width
; j
++) {
317 acc
[j
* 4 + 0] += rgba
[j
][RCOMP
];
318 acc
[j
* 4 + 1] += rgba
[j
][GCOMP
];
319 acc
[j
* 4 + 2] += rgba
[j
][BCOMP
];
320 acc
[j
* 4 + 3] += rgba
[j
][ACOMP
];
324 /* scaled integer (or float) accum buffer */
326 for (j
= 0; j
< width
; j
++) {
327 acc
[j
* 4 + 0] += (GLshort
) ((GLfloat
) rgba
[j
][RCOMP
] * scale
);
328 acc
[j
* 4 + 1] += (GLshort
) ((GLfloat
) rgba
[j
][GCOMP
] * scale
);
329 acc
[j
* 4 + 2] += (GLshort
) ((GLfloat
) rgba
[j
][BCOMP
] * scale
);
330 acc
[j
* 4 + 3] += (GLshort
) ((GLfloat
) rgba
[j
][ACOMP
] * scale
);
335 rb
->PutRow(ctx
, rb
, width
, xpos
, ypos
+ i
, accumRow
, NULL
);
340 /* other types someday */
347 accum_load(GLcontext
*ctx
, GLfloat value
,
348 GLint xpos
, GLint ypos
, GLint width
, GLint height
)
350 SWcontext
*swrast
= SWRAST_CONTEXT(ctx
);
351 struct gl_renderbuffer
*rb
352 = ctx
->DrawBuffer
->Attachment
[BUFFER_ACCUM
].Renderbuffer
;
353 const GLboolean directAccess
= (rb
->GetPointer(ctx
, rb
, 0, 0) != NULL
);
357 if (!ctx
->ReadBuffer
->_ColorReadBuffer
) {
358 /* no read buffer - OK */
362 /* This is a change to go into optimized accum buffer mode */
363 if (value
> 0.0 && value
<= 1.0) {
364 #if USE_OPTIMIZED_ACCUM
365 swrast
->_IntegerAccumMode
= GL_TRUE
;
367 swrast
->_IntegerAccumMode
= GL_FALSE
;
369 swrast
->_IntegerAccumScaler
= value
;
372 swrast
->_IntegerAccumMode
= GL_FALSE
;
373 swrast
->_IntegerAccumScaler
= 0.0;
376 if (rb
->DataType
== GL_SHORT
|| rb
->DataType
== GL_UNSIGNED_SHORT
) {
377 const GLfloat scale
= value
* ACCUM_SCALE16
/ CHAN_MAXF
;
378 GLshort accumRow
[4 * MAX_WIDTH
];
379 GLchan rgba
[MAX_WIDTH
][4];
382 for (i
= 0; i
< height
; i
++) {
385 acc
= (GLshort
*) rb
->GetPointer(ctx
, rb
, xpos
, ypos
+ i
);
388 rb
->GetRow(ctx
, rb
, width
, xpos
, ypos
+ i
, accumRow
);
392 /* read colors from color buffer */
393 _swrast_read_rgba_span(ctx
, ctx
->ReadBuffer
->_ColorReadBuffer
, width
,
394 xpos
, ypos
+ i
, rgba
);
397 if (swrast
->_IntegerAccumMode
) {
398 /* just copy values in */
400 assert(swrast
->_IntegerAccumScaler
> 0.0);
401 assert(swrast
->_IntegerAccumScaler
<= 1.0);
402 for (j
= 0; j
< width
; j
++) {
403 acc
[j
* 4 + 0] = rgba
[j
][RCOMP
];
404 acc
[j
* 4 + 1] = rgba
[j
][GCOMP
];
405 acc
[j
* 4 + 2] = rgba
[j
][BCOMP
];
406 acc
[j
* 4 + 3] = rgba
[j
][ACOMP
];
410 /* scaled integer (or float) accum buffer */
412 for (j
= 0; j
< width
; j
++) {
413 acc
[j
* 4 + 0] = (GLshort
) ((GLfloat
) rgba
[j
][RCOMP
] * scale
);
414 acc
[j
* 4 + 1] = (GLshort
) ((GLfloat
) rgba
[j
][GCOMP
] * scale
);
415 acc
[j
* 4 + 2] = (GLshort
) ((GLfloat
) rgba
[j
][BCOMP
] * scale
);
416 acc
[j
* 4 + 3] = (GLshort
) ((GLfloat
) rgba
[j
][ACOMP
] * scale
);
421 rb
->PutRow(ctx
, rb
, width
, xpos
, ypos
+ i
, accumRow
, NULL
);
429 accum_return(GLcontext
*ctx
, GLfloat value
,
430 GLint xpos
, GLint ypos
, GLint width
, GLint height
)
432 SWcontext
*swrast
= SWRAST_CONTEXT(ctx
);
433 struct gl_framebuffer
*fb
= ctx
->DrawBuffer
;
434 struct gl_renderbuffer
*accumRb
= fb
->Attachment
[BUFFER_ACCUM
].Renderbuffer
;
435 const GLboolean directAccess
436 = (accumRb
->GetPointer(ctx
, accumRb
, 0, 0) != NULL
);
437 const GLboolean masking
= (!ctx
->Color
.ColorMask
[RCOMP
] ||
438 !ctx
->Color
.ColorMask
[GCOMP
] ||
439 !ctx
->Color
.ColorMask
[BCOMP
] ||
440 !ctx
->Color
.ColorMask
[ACOMP
]);
442 static GLchan multTable
[32768];
443 static GLfloat prevMult
= 0.0;
444 const GLfloat mult
= swrast
->_IntegerAccumScaler
;
445 const GLint max
= MIN2((GLint
) (256 / mult
), 32767);
447 /* May have to leave optimized accum buffer mode */
448 if (swrast
->_IntegerAccumMode
&& value
!= 1.0)
451 if (swrast
->_IntegerAccumMode
&& swrast
->_IntegerAccumScaler
> 0) {
452 /* build lookup table to avoid many floating point multiplies */
454 assert(swrast
->_IntegerAccumScaler
<= 1.0);
455 if (mult
!= prevMult
) {
456 for (j
= 0; j
< max
; j
++)
457 multTable
[j
] = IROUND((GLfloat
) j
* mult
);
462 if (accumRb
->DataType
== GL_SHORT
||
463 accumRb
->DataType
== GL_UNSIGNED_SHORT
) {
464 const GLfloat scale
= value
* CHAN_MAXF
/ ACCUM_SCALE16
;
468 /* XXX maybe transpose the 'i' and 'buffer' loops??? */
469 for (i
= 0; i
< height
; i
++) {
470 GLchan rgba
[MAX_WIDTH
][4];
471 GLshort accumRow
[4 * MAX_WIDTH
];
475 acc
= (GLshort
*) accumRb
->GetPointer(ctx
, accumRb
, xpos
, ypos
+i
);
478 accumRb
->GetRow(ctx
, accumRb
, width
, xpos
, ypos
+ i
, accumRow
);
482 /* get the colors to return */
483 if (swrast
->_IntegerAccumMode
) {
485 for (j
= 0; j
< width
; j
++) {
486 ASSERT(acc
[j
* 4 + 0] < max
);
487 ASSERT(acc
[j
* 4 + 1] < max
);
488 ASSERT(acc
[j
* 4 + 2] < max
);
489 ASSERT(acc
[j
* 4 + 3] < max
);
490 rgba
[j
][RCOMP
] = multTable
[acc
[j
* 4 + 0]];
491 rgba
[j
][GCOMP
] = multTable
[acc
[j
* 4 + 1]];
492 rgba
[j
][BCOMP
] = multTable
[acc
[j
* 4 + 2]];
493 rgba
[j
][ACOMP
] = multTable
[acc
[j
* 4 + 3]];
497 /* scaled integer (or float) accum buffer */
499 for (j
= 0; j
< width
; j
++) {
501 GLchan r
= acc
[j
* 4 + 0] * scale
;
502 GLchan g
= acc
[j
* 4 + 1] * scale
;
503 GLchan b
= acc
[j
* 4 + 2] * scale
;
504 GLchan a
= acc
[j
* 4 + 3] * scale
;
506 GLint r
= IROUND( (GLfloat
) (acc
[j
* 4 + 0]) * scale
);
507 GLint g
= IROUND( (GLfloat
) (acc
[j
* 4 + 1]) * scale
);
508 GLint b
= IROUND( (GLfloat
) (acc
[j
* 4 + 2]) * scale
);
509 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
);
524 rb
->PutRow(ctx
, rb
, width
, xpos
, ypos
+ i
, rgba
, NULL
);
529 /* other types someday */
536 * Software fallback for glAccum.
539 _swrast_Accum( GLcontext
*ctx
, GLenum op
, GLfloat value
,
540 GLint xpos
, GLint ypos
,
541 GLint width
, GLint height
)
544 SWcontext
*swrast
= SWRAST_CONTEXT(ctx
);
546 if (SWRAST_CONTEXT(ctx
)->NewState
)
547 _swrast_validate_derived( ctx
);
549 if (!ctx
->DrawBuffer
->Attachment
[BUFFER_ACCUM
].Renderbuffer
) {
550 _mesa_warning(ctx
, "Calling glAccum() without an accumulation buffer");
554 RENDER_START(swrast
, ctx
);
559 accum_add(ctx
, value
, xpos
, ypos
, width
, height
);
564 accum_mult(ctx
, value
, xpos
, ypos
, width
, height
);
569 accum_accum(ctx
, value
, xpos
, ypos
, width
, height
);
573 accum_load(ctx
, value
, xpos
, ypos
, width
, height
);
576 accum_return(ctx
, value
, xpos
, ypos
, width
, height
);
579 _mesa_problem(ctx
, "invalid mode in _swrast_Accum()");
583 RENDER_FINISH(swrast
, ctx
);