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.0F
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( struct gl_context
*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( struct gl_context
*ctx
, struct gl_renderbuffer
*rb
)
130 SWcontext
*swrast
= SWRAST_CONTEXT(ctx
);
131 GLuint x
, y
, width
, height
;
133 /* No accumulation buffer! Not an error. */
134 if (!rb
|| !rb
->Data
)
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(struct gl_context
*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(struct gl_context
*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(struct gl_context
*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 if (rb
->DataType
== GL_SHORT
|| rb
->DataType
== GL_UNSIGNED_SHORT
) {
290 const GLfloat scale
= value
* ACCUM_SCALE16
/ CHAN_MAXF
;
291 GLshort accumRow
[4 * MAX_WIDTH
];
292 GLchan rgba
[MAX_WIDTH
][4];
295 for (i
= 0; i
< height
; i
++) {
298 acc
= (GLshort
*) rb
->GetPointer(ctx
, rb
, xpos
, ypos
+ i
);
301 rb
->GetRow(ctx
, rb
, width
, xpos
, ypos
+ i
, accumRow
);
305 /* read colors from color buffer */
306 _swrast_read_rgba_span(ctx
, ctx
->ReadBuffer
->_ColorReadBuffer
, width
,
307 xpos
, ypos
+ i
, CHAN_TYPE
, rgba
);
309 /* do accumulation */
310 if (swrast
->_IntegerAccumMode
) {
311 /* simply add integer color values into accum buffer */
313 for (j
= 0; j
< width
; j
++) {
314 acc
[j
* 4 + 0] += rgba
[j
][RCOMP
];
315 acc
[j
* 4 + 1] += rgba
[j
][GCOMP
];
316 acc
[j
* 4 + 2] += rgba
[j
][BCOMP
];
317 acc
[j
* 4 + 3] += rgba
[j
][ACOMP
];
321 /* scaled integer (or float) accum buffer */
323 for (j
= 0; j
< width
; j
++) {
324 acc
[j
* 4 + 0] += (GLshort
) ((GLfloat
) rgba
[j
][RCOMP
] * scale
);
325 acc
[j
* 4 + 1] += (GLshort
) ((GLfloat
) rgba
[j
][GCOMP
] * scale
);
326 acc
[j
* 4 + 2] += (GLshort
) ((GLfloat
) rgba
[j
][BCOMP
] * scale
);
327 acc
[j
* 4 + 3] += (GLshort
) ((GLfloat
) rgba
[j
][ACOMP
] * scale
);
332 rb
->PutRow(ctx
, rb
, width
, xpos
, ypos
+ i
, accumRow
, NULL
);
337 /* other types someday */
344 accum_load(struct gl_context
*ctx
, GLfloat value
,
345 GLint xpos
, GLint ypos
, GLint width
, GLint height
)
347 SWcontext
*swrast
= SWRAST_CONTEXT(ctx
);
348 struct gl_renderbuffer
*rb
349 = ctx
->DrawBuffer
->Attachment
[BUFFER_ACCUM
].Renderbuffer
;
350 const GLboolean directAccess
= (rb
->GetPointer(ctx
, rb
, 0, 0) != NULL
);
354 if (!ctx
->ReadBuffer
->_ColorReadBuffer
) {
355 /* no read buffer - OK */
359 /* This is a change to go into optimized accum buffer mode */
360 if (value
> 0.0 && value
<= 1.0) {
361 #if USE_OPTIMIZED_ACCUM
362 swrast
->_IntegerAccumMode
= GL_TRUE
;
364 swrast
->_IntegerAccumMode
= GL_FALSE
;
366 swrast
->_IntegerAccumScaler
= value
;
369 swrast
->_IntegerAccumMode
= GL_FALSE
;
370 swrast
->_IntegerAccumScaler
= 0.0;
373 if (rb
->DataType
== GL_SHORT
|| rb
->DataType
== GL_UNSIGNED_SHORT
) {
374 const GLfloat scale
= value
* ACCUM_SCALE16
/ CHAN_MAXF
;
375 GLshort accumRow
[4 * MAX_WIDTH
];
376 GLchan rgba
[MAX_WIDTH
][4];
379 for (i
= 0; i
< height
; i
++) {
382 acc
= (GLshort
*) rb
->GetPointer(ctx
, rb
, xpos
, ypos
+ i
);
385 rb
->GetRow(ctx
, rb
, width
, xpos
, ypos
+ i
, accumRow
);
389 /* read colors from color buffer */
390 _swrast_read_rgba_span(ctx
, ctx
->ReadBuffer
->_ColorReadBuffer
, width
,
391 xpos
, ypos
+ i
, CHAN_TYPE
, rgba
);
394 if (swrast
->_IntegerAccumMode
) {
395 /* just copy values in */
397 assert(swrast
->_IntegerAccumScaler
> 0.0);
398 assert(swrast
->_IntegerAccumScaler
<= 1.0);
399 for (j
= 0; j
< width
; j
++) {
400 acc
[j
* 4 + 0] = rgba
[j
][RCOMP
];
401 acc
[j
* 4 + 1] = rgba
[j
][GCOMP
];
402 acc
[j
* 4 + 2] = rgba
[j
][BCOMP
];
403 acc
[j
* 4 + 3] = rgba
[j
][ACOMP
];
407 /* scaled integer (or float) accum buffer */
409 for (j
= 0; j
< width
; j
++) {
410 acc
[j
* 4 + 0] = (GLshort
) ((GLfloat
) rgba
[j
][RCOMP
] * scale
);
411 acc
[j
* 4 + 1] = (GLshort
) ((GLfloat
) rgba
[j
][GCOMP
] * scale
);
412 acc
[j
* 4 + 2] = (GLshort
) ((GLfloat
) rgba
[j
][BCOMP
] * scale
);
413 acc
[j
* 4 + 3] = (GLshort
) ((GLfloat
) rgba
[j
][ACOMP
] * scale
);
418 rb
->PutRow(ctx
, rb
, width
, xpos
, ypos
+ i
, accumRow
, NULL
);
426 accum_return(struct gl_context
*ctx
, GLfloat value
,
427 GLint xpos
, GLint ypos
, GLint width
, GLint height
)
429 SWcontext
*swrast
= SWRAST_CONTEXT(ctx
);
430 struct gl_framebuffer
*fb
= ctx
->DrawBuffer
;
431 struct gl_renderbuffer
*accumRb
= fb
->Attachment
[BUFFER_ACCUM
].Renderbuffer
;
432 const GLboolean directAccess
433 = (accumRb
->GetPointer(ctx
, accumRb
, 0, 0) != NULL
);
435 static GLchan multTable
[32768];
436 static GLfloat prevMult
= 0.0;
437 const GLfloat mult
= swrast
->_IntegerAccumScaler
;
438 const GLint max
= MIN2((GLint
) (256 / mult
), 32767);
440 /* May have to leave optimized accum buffer mode */
441 if (swrast
->_IntegerAccumMode
&& value
!= 1.0)
444 if (swrast
->_IntegerAccumMode
&& swrast
->_IntegerAccumScaler
> 0) {
445 /* build lookup table to avoid many floating point multiplies */
447 assert(swrast
->_IntegerAccumScaler
<= 1.0);
448 if (mult
!= prevMult
) {
449 for (j
= 0; j
< max
; j
++)
450 multTable
[j
] = IROUND((GLfloat
) j
* mult
);
455 if (accumRb
->DataType
== GL_SHORT
||
456 accumRb
->DataType
== GL_UNSIGNED_SHORT
) {
457 const GLfloat scale
= value
* CHAN_MAXF
/ ACCUM_SCALE16
;
461 /* XXX maybe transpose the 'i' and 'buffer' loops??? */
462 for (i
= 0; i
< height
; i
++) {
463 GLshort accumRow
[4 * MAX_WIDTH
];
467 /* init color span */
468 INIT_SPAN(span
, GL_BITMAP
);
470 span
.arrayMask
= SPAN_RGBA
;
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 span
.array
->rgba
[j
][RCOMP
] = multTable
[acc
[j
* 4 + 0]];
491 span
.array
->rgba
[j
][GCOMP
] = multTable
[acc
[j
* 4 + 1]];
492 span
.array
->rgba
[j
][BCOMP
] = multTable
[acc
[j
* 4 + 2]];
493 span
.array
->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 span
.array
->rgba
[j
][RCOMP
] = CLAMP( r
, 0, CHAN_MAX
);
512 span
.array
->rgba
[j
][GCOMP
] = CLAMP( g
, 0, CHAN_MAX
);
513 span
.array
->rgba
[j
][BCOMP
] = CLAMP( b
, 0, CHAN_MAX
);
514 span
.array
->rgba
[j
][ACOMP
] = CLAMP( a
, 0, CHAN_MAX
);
519 for (buffer
= 0; buffer
< fb
->_NumColorDrawBuffers
; buffer
++) {
520 struct gl_renderbuffer
*rb
= fb
->_ColorDrawBuffers
[buffer
];
521 const GLboolean masking
= (!ctx
->Color
.ColorMask
[buffer
][RCOMP
] ||
522 !ctx
->Color
.ColorMask
[buffer
][GCOMP
] ||
523 !ctx
->Color
.ColorMask
[buffer
][BCOMP
] ||
524 !ctx
->Color
.ColorMask
[buffer
][ACOMP
]);
526 _swrast_mask_rgba_span(ctx
, rb
, &span
, buffer
);
528 rb
->PutRow(ctx
, rb
, width
, xpos
, ypos
+ i
, span
.array
->rgba
, NULL
);
533 /* other types someday */
540 * Software fallback for glAccum.
543 _swrast_Accum(struct gl_context
*ctx
, GLenum op
, GLfloat value
)
545 SWcontext
*swrast
= SWRAST_CONTEXT(ctx
);
546 GLint xpos
, ypos
, width
, height
;
548 if (swrast
->NewState
)
549 _swrast_validate_derived( ctx
);
551 if (!ctx
->DrawBuffer
->Attachment
[BUFFER_ACCUM
].Renderbuffer
) {
552 _mesa_warning(ctx
, "Calling glAccum() without an accumulation buffer");
556 swrast_render_start(ctx
);
558 /* Compute region after calling swrast_render_start() so that we know the
559 * drawbuffer's size/bounds are up to date.
561 xpos
= ctx
->DrawBuffer
->_Xmin
;
562 ypos
= ctx
->DrawBuffer
->_Ymin
;
563 width
= ctx
->DrawBuffer
->_Xmax
- ctx
->DrawBuffer
->_Xmin
;
564 height
= ctx
->DrawBuffer
->_Ymax
- ctx
->DrawBuffer
->_Ymin
;
569 accum_add(ctx
, value
, xpos
, ypos
, width
, height
);
574 accum_mult(ctx
, value
, xpos
, ypos
, width
, height
);
579 accum_accum(ctx
, value
, xpos
, ypos
, width
, height
);
583 accum_load(ctx
, value
, xpos
, ypos
, width
, height
);
586 accum_return(ctx
, value
, xpos
, ypos
, width
, height
);
589 _mesa_problem(ctx
, "invalid mode in _swrast_Accum()");
593 swrast_render_finish(ctx
);