• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Mesa 3-D graphics library
3  *
4  * Copyright (C) 1999-2005  Brian Paul   All Rights Reserved.
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a
7  * copy of this software and associated documentation files (the "Software"),
8  * to deal in the Software without restriction, including without limitation
9  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10  * and/or sell copies of the Software, and to permit persons to whom the
11  * Software is furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included
14  * in all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
19  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22  * OTHER DEALINGS IN THE SOFTWARE.
23  */
24 
25 #include "glheader.h"
26 #include "accum.h"
27 #include "condrender.h"
28 #include "context.h"
29 #include "format_unpack.h"
30 #include "format_pack.h"
31 #include "framebuffer.h"
32 #include "renderbuffer.h"
33 #include "macros.h"
34 #include "state.h"
35 #include "mtypes.h"
36 #include "api_exec_decl.h"
37 
38 void GLAPIENTRY
_mesa_ClearAccum(GLfloat red,GLfloat green,GLfloat blue,GLfloat alpha)39 _mesa_ClearAccum( GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha )
40 {
41    GLfloat tmp[4];
42    GET_CURRENT_CONTEXT(ctx);
43 
44    tmp[0] = CLAMP( red,   -1.0F, 1.0F );
45    tmp[1] = CLAMP( green, -1.0F, 1.0F );
46    tmp[2] = CLAMP( blue,  -1.0F, 1.0F );
47    tmp[3] = CLAMP( alpha, -1.0F, 1.0F );
48 
49    if (TEST_EQ_4V(tmp, ctx->Accum.ClearColor))
50       return;
51 
52    ctx->PopAttribState |= GL_ACCUM_BUFFER_BIT;
53    COPY_4FV( ctx->Accum.ClearColor, tmp );
54 }
55 
56 
57 /**
58  * Clear the accumulation buffer by mapping the renderbuffer and
59  * writing the clear color to it.  Called by the driver's implementation
60  * of the glClear function.
61  */
62 void
_mesa_clear_accum_buffer(struct gl_context * ctx)63 _mesa_clear_accum_buffer(struct gl_context *ctx)
64 {
65    GLuint x, y, width, height;
66    GLubyte *accMap;
67    GLint accRowStride;
68    struct gl_renderbuffer *accRb;
69 
70    if (!ctx->DrawBuffer)
71       return;
72 
73    accRb = ctx->DrawBuffer->Attachment[BUFFER_ACCUM].Renderbuffer;
74    if (!accRb)
75       return;   /* missing accum buffer, not an error */
76 
77    _mesa_update_draw_buffer_bounds(ctx, ctx->DrawBuffer);
78 
79    /* bounds, with scissor */
80    x = ctx->DrawBuffer->_Xmin;
81    y = ctx->DrawBuffer->_Ymin;
82    width = ctx->DrawBuffer->_Xmax - ctx->DrawBuffer->_Xmin;
83    height = ctx->DrawBuffer->_Ymax - ctx->DrawBuffer->_Ymin;
84 
85    _mesa_map_renderbuffer(ctx, accRb, x, y, width, height,
86                       GL_MAP_WRITE_BIT, &accMap, &accRowStride,
87                       ctx->DrawBuffer->FlipY);
88 
89    if (!accMap) {
90       _mesa_error(ctx, GL_OUT_OF_MEMORY, "glAccum");
91       return;
92    }
93 
94    if (accRb->Format == MESA_FORMAT_RGBA_SNORM16) {
95       const GLshort clearR = FLOAT_TO_SHORT(ctx->Accum.ClearColor[0]);
96       const GLshort clearG = FLOAT_TO_SHORT(ctx->Accum.ClearColor[1]);
97       const GLshort clearB = FLOAT_TO_SHORT(ctx->Accum.ClearColor[2]);
98       const GLshort clearA = FLOAT_TO_SHORT(ctx->Accum.ClearColor[3]);
99       GLuint i, j;
100 
101       for (j = 0; j < height; j++) {
102          GLshort *row = (GLshort *) accMap;
103 
104          for (i = 0; i < width; i++) {
105             row[i * 4 + 0] = clearR;
106             row[i * 4 + 1] = clearG;
107             row[i * 4 + 2] = clearB;
108             row[i * 4 + 3] = clearA;
109          }
110          accMap += accRowStride;
111       }
112    }
113    else {
114       /* other types someday? */
115       _mesa_warning(ctx, "unexpected accum buffer type");
116    }
117 
118    _mesa_unmap_renderbuffer(ctx, accRb);
119 }
120 
121 
122 /**
123  * if (bias)
124  *    Accum += value
125  * else
126  *    Accum *= value
127  */
128 static void
accum_scale_or_bias(struct gl_context * ctx,GLfloat value,GLint xpos,GLint ypos,GLint width,GLint height,GLboolean bias)129 accum_scale_or_bias(struct gl_context *ctx, GLfloat value,
130                     GLint xpos, GLint ypos, GLint width, GLint height,
131                     GLboolean bias)
132 {
133    struct gl_renderbuffer *accRb =
134       ctx->DrawBuffer->Attachment[BUFFER_ACCUM].Renderbuffer;
135    GLubyte *accMap;
136    GLint accRowStride;
137 
138    assert(accRb);
139 
140    _mesa_map_renderbuffer(ctx, accRb, xpos, ypos, width, height,
141                       GL_MAP_READ_BIT | GL_MAP_WRITE_BIT,
142                       &accMap, &accRowStride,
143                       ctx->DrawBuffer->FlipY);
144 
145    if (!accMap) {
146       _mesa_error(ctx, GL_OUT_OF_MEMORY, "glAccum");
147       return;
148    }
149 
150    if (accRb->Format == MESA_FORMAT_RGBA_SNORM16) {
151       const GLshort incr = (GLshort) (value * 32767.0f);
152       GLint i, j;
153       if (bias) {
154          for (j = 0; j < height; j++) {
155             GLshort *acc = (GLshort *) accMap;
156             for (i = 0; i < 4 * width; i++) {
157                acc[i] += incr;
158             }
159             accMap += accRowStride;
160          }
161       }
162       else {
163          /* scale */
164          for (j = 0; j < height; j++) {
165             GLshort *acc = (GLshort *) accMap;
166             for (i = 0; i < 4 * width; i++) {
167                acc[i] = (GLshort) (acc[i] * value);
168             }
169             accMap += accRowStride;
170          }
171       }
172    }
173    else {
174       /* other types someday? */
175    }
176 
177    _mesa_unmap_renderbuffer(ctx, accRb);
178 }
179 
180 
181 /**
182  * if (load)
183  *    Accum = ColorBuf * value
184  * else
185  *    Accum += ColorBuf * value
186  */
187 static void
accum_or_load(struct gl_context * ctx,GLfloat value,GLint xpos,GLint ypos,GLint width,GLint height,GLboolean load)188 accum_or_load(struct gl_context *ctx, GLfloat value,
189               GLint xpos, GLint ypos, GLint width, GLint height,
190               GLboolean load)
191 {
192    struct gl_renderbuffer *accRb =
193       ctx->DrawBuffer->Attachment[BUFFER_ACCUM].Renderbuffer;
194    struct gl_renderbuffer *colorRb = ctx->ReadBuffer->_ColorReadBuffer;
195    GLubyte *accMap, *colorMap;
196    GLint accRowStride, colorRowStride;
197    GLbitfield mappingFlags;
198 
199    if (!colorRb) {
200       /* no read buffer - OK */
201       return;
202    }
203 
204    assert(accRb);
205 
206    mappingFlags = GL_MAP_WRITE_BIT;
207    if (!load) /* if we're accumulating */
208       mappingFlags |= GL_MAP_READ_BIT;
209 
210    /* Map accum buffer */
211    _mesa_map_renderbuffer(ctx, accRb, xpos, ypos, width, height,
212                       mappingFlags, &accMap, &accRowStride,
213                       ctx->DrawBuffer->FlipY);
214    if (!accMap) {
215       _mesa_error(ctx, GL_OUT_OF_MEMORY, "glAccum");
216       return;
217    }
218 
219    /* Map color buffer */
220    _mesa_map_renderbuffer(ctx, colorRb, xpos, ypos, width, height,
221                       GL_MAP_READ_BIT,
222                       &colorMap, &colorRowStride,
223                       ctx->DrawBuffer->FlipY);
224    if (!colorMap) {
225       _mesa_unmap_renderbuffer(ctx, accRb);
226       _mesa_error(ctx, GL_OUT_OF_MEMORY, "glAccum");
227       return;
228    }
229 
230    if (accRb->Format == MESA_FORMAT_RGBA_SNORM16) {
231       const GLfloat scale = value * 32767.0f;
232       GLint i, j;
233       GLfloat (*rgba)[4];
234 
235       rgba = malloc(width * 4 * sizeof(GLfloat));
236       if (rgba) {
237          for (j = 0; j < height; j++) {
238             GLshort *acc = (GLshort *) accMap;
239 
240             /* read colors from source color buffer */
241             _mesa_unpack_rgba_row(colorRb->Format, width, colorMap, rgba);
242 
243             if (load) {
244                for (i = 0; i < width; i++) {
245                   acc[i * 4 + 0] = (GLshort) (rgba[i][RCOMP] * scale);
246                   acc[i * 4 + 1] = (GLshort) (rgba[i][GCOMP] * scale);
247                   acc[i * 4 + 2] = (GLshort) (rgba[i][BCOMP] * scale);
248                   acc[i * 4 + 3] = (GLshort) (rgba[i][ACOMP] * scale);
249                }
250             }
251             else {
252                /* accumulate */
253                for (i = 0; i < width; i++) {
254                   acc[i * 4 + 0] += (GLshort) (rgba[i][RCOMP] * scale);
255                   acc[i * 4 + 1] += (GLshort) (rgba[i][GCOMP] * scale);
256                   acc[i * 4 + 2] += (GLshort) (rgba[i][BCOMP] * scale);
257                   acc[i * 4 + 3] += (GLshort) (rgba[i][ACOMP] * scale);
258                }
259             }
260 
261             colorMap += colorRowStride;
262             accMap += accRowStride;
263          }
264 
265          free(rgba);
266       }
267       else {
268          _mesa_error(ctx, GL_OUT_OF_MEMORY, "glAccum");
269       }
270    }
271    else {
272       /* other types someday? */
273    }
274 
275    _mesa_unmap_renderbuffer(ctx, accRb);
276    _mesa_unmap_renderbuffer(ctx, colorRb);
277 }
278 
279 
280 /**
281  * ColorBuffer = Accum * value
282  */
283 static void
accum_return(struct gl_context * ctx,GLfloat value,GLint xpos,GLint ypos,GLint width,GLint height)284 accum_return(struct gl_context *ctx, GLfloat value,
285              GLint xpos, GLint ypos, GLint width, GLint height)
286 {
287    struct gl_framebuffer *fb = ctx->DrawBuffer;
288    struct gl_renderbuffer *accRb = fb->Attachment[BUFFER_ACCUM].Renderbuffer;
289    GLubyte *accMap, *colorMap;
290    GLint accRowStride, colorRowStride;
291    GLuint buffer;
292 
293    /* Map accum buffer */
294    _mesa_map_renderbuffer(ctx, accRb, xpos, ypos, width, height,
295                                GL_MAP_READ_BIT,
296                                &accMap, &accRowStride, fb->FlipY);
297    if (!accMap) {
298       _mesa_error(ctx, GL_OUT_OF_MEMORY, "glAccum");
299       return;
300    }
301 
302    /* Loop over destination buffers */
303    for (buffer = 0; buffer < fb->_NumColorDrawBuffers; buffer++) {
304       struct gl_renderbuffer *colorRb = fb->_ColorDrawBuffers[buffer];
305       const GLboolean masking = (!GET_COLORMASK_BIT(ctx->Color.ColorMask, buffer, 0) ||
306                                  !GET_COLORMASK_BIT(ctx->Color.ColorMask, buffer, 1) ||
307                                  !GET_COLORMASK_BIT(ctx->Color.ColorMask, buffer, 2) ||
308                                  !GET_COLORMASK_BIT(ctx->Color.ColorMask, buffer, 3));
309       GLbitfield mappingFlags = GL_MAP_WRITE_BIT;
310 
311       if (masking)
312          mappingFlags |= GL_MAP_READ_BIT;
313 
314       /* Map color buffer */
315       _mesa_map_renderbuffer(ctx, colorRb, xpos, ypos, width, height,
316                                   mappingFlags, &colorMap, &colorRowStride,
317                                   fb->FlipY);
318       if (!colorMap) {
319          _mesa_error(ctx, GL_OUT_OF_MEMORY, "glAccum");
320          continue;
321       }
322 
323       if (accRb->Format == MESA_FORMAT_RGBA_SNORM16) {
324          const GLfloat scale = value / 32767.0f;
325          GLint i, j;
326          GLfloat (*rgba)[4], (*dest)[4];
327 
328          rgba = malloc(width * 4 * sizeof(GLfloat));
329          dest = malloc(width * 4 * sizeof(GLfloat));
330 
331          if (rgba && dest) {
332             for (j = 0; j < height; j++) {
333                GLshort *acc = (GLshort *) accMap;
334 
335                for (i = 0; i < width; i++) {
336                   rgba[i][0] = acc[i * 4 + 0] * scale;
337                   rgba[i][1] = acc[i * 4 + 1] * scale;
338                   rgba[i][2] = acc[i * 4 + 2] * scale;
339                   rgba[i][3] = acc[i * 4 + 3] * scale;
340                }
341 
342                if (masking) {
343 
344                   /* get existing colors from dest buffer */
345                   _mesa_unpack_rgba_row(colorRb->Format, width, colorMap, dest);
346 
347                   /* use the dest colors where mask[channel] = 0 */
348                   if (!GET_COLORMASK_BIT(ctx->Color.ColorMask, buffer, 0)) {
349                      for (i = 0; i < width; i++)
350                         rgba[i][RCOMP] = dest[i][RCOMP];
351                   }
352                   if (!GET_COLORMASK_BIT(ctx->Color.ColorMask, buffer, 1)) {
353                      for (i = 0; i < width; i++)
354                         rgba[i][GCOMP] = dest[i][GCOMP];
355                   }
356                   if (!GET_COLORMASK_BIT(ctx->Color.ColorMask, buffer, 2)) {
357                      for (i = 0; i < width; i++)
358                         rgba[i][BCOMP] = dest[i][BCOMP];
359                   }
360                   if (!GET_COLORMASK_BIT(ctx->Color.ColorMask, buffer, 3)) {
361                      for (i = 0; i < width; i++)
362                         rgba[i][ACOMP] = dest[i][ACOMP];
363                   }
364                }
365 
366                _mesa_pack_float_rgba_row(colorRb->Format, width,
367                                          (const GLfloat (*)[4]) rgba, colorMap);
368 
369                accMap += accRowStride;
370                colorMap += colorRowStride;
371             }
372          }
373          else {
374             _mesa_error(ctx, GL_OUT_OF_MEMORY, "glAccum");
375          }
376          free(rgba);
377          free(dest);
378       }
379       else {
380          /* other types someday? */
381       }
382 
383       _mesa_unmap_renderbuffer(ctx, colorRb);
384    }
385 
386    _mesa_unmap_renderbuffer(ctx, accRb);
387 }
388 
389 
390 
391 /**
392  * Software fallback for glAccum.  A hardware driver that supports
393  * signed 16-bit color channels could implement hardware accumulation
394  * operations, but no driver does so at this time.
395  */
396 static void
accum(struct gl_context * ctx,GLenum op,GLfloat value)397 accum(struct gl_context *ctx, GLenum op, GLfloat value)
398 {
399    GLint xpos, ypos, width, height;
400 
401    if (!ctx->DrawBuffer->Attachment[BUFFER_ACCUM].Renderbuffer) {
402       _mesa_warning(ctx, "Calling glAccum() without an accumulation buffer");
403       return;
404    }
405 
406    if (!_mesa_check_conditional_render(ctx))
407       return;
408 
409    _mesa_update_draw_buffer_bounds(ctx, ctx->DrawBuffer);
410 
411    xpos = ctx->DrawBuffer->_Xmin;
412    ypos = ctx->DrawBuffer->_Ymin;
413    width =  ctx->DrawBuffer->_Xmax - ctx->DrawBuffer->_Xmin;
414    height = ctx->DrawBuffer->_Ymax - ctx->DrawBuffer->_Ymin;
415 
416    switch (op) {
417    case GL_ADD:
418       if (value != 0.0F) {
419          accum_scale_or_bias(ctx, value, xpos, ypos, width, height, GL_TRUE);
420       }
421       break;
422    case GL_MULT:
423       if (value != 1.0F) {
424          accum_scale_or_bias(ctx, value, xpos, ypos, width, height, GL_FALSE);
425       }
426       break;
427    case GL_ACCUM:
428       if (value != 0.0F) {
429          accum_or_load(ctx, value, xpos, ypos, width, height, GL_FALSE);
430       }
431       break;
432    case GL_LOAD:
433       accum_or_load(ctx, value, xpos, ypos, width, height, GL_TRUE);
434       break;
435    case GL_RETURN:
436       accum_return(ctx, value, xpos, ypos, width, height);
437       break;
438    default:
439       unreachable("invalid mode in _mesa_Accum()");
440       break;
441    }
442 }
443 
444 
445 void
_mesa_init_accum(struct gl_context * ctx)446 _mesa_init_accum( struct gl_context *ctx )
447 {
448    /* Accumulate buffer group */
449    ASSIGN_4V( ctx->Accum.ClearColor, 0.0, 0.0, 0.0, 0.0 );
450 }
451 
452 
453 void GLAPIENTRY
_mesa_Accum(GLenum op,GLfloat value)454 _mesa_Accum( GLenum op, GLfloat value )
455 {
456    GET_CURRENT_CONTEXT(ctx);
457    FLUSH_VERTICES(ctx, 0, 0);
458 
459    switch (op) {
460    case GL_ADD:
461    case GL_MULT:
462    case GL_ACCUM:
463    case GL_LOAD:
464    case GL_RETURN:
465       /* OK */
466       break;
467    default:
468       _mesa_error(ctx, GL_INVALID_ENUM, "glAccum(op)");
469       return;
470    }
471 
472    if (ctx->DrawBuffer->Visual.accumRedBits == 0) {
473       _mesa_error(ctx, GL_INVALID_OPERATION, "glAccum(no accum buffer)");
474       return;
475    }
476 
477    if (ctx->DrawBuffer != ctx->ReadBuffer) {
478       /* See GLX_SGI_make_current_read or WGL_ARB_make_current_read,
479        * or GL_EXT_framebuffer_blit.
480        */
481       _mesa_error(ctx, GL_INVALID_OPERATION,
482                   "glAccum(different read/draw buffers)");
483       return;
484    }
485 
486    if (ctx->NewState)
487       _mesa_update_state(ctx);
488 
489    if (ctx->DrawBuffer->_Status != GL_FRAMEBUFFER_COMPLETE_EXT) {
490       _mesa_error(ctx, GL_INVALID_FRAMEBUFFER_OPERATION_EXT,
491                   "glAccum(incomplete framebuffer)");
492       return;
493    }
494 
495    if (ctx->RasterDiscard)
496       return;
497 
498    if (ctx->RenderMode == GL_RENDER) {
499       accum(ctx, op, value);
500    }
501 }
502