• 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 "imports.h"
33 #include "macros.h"
34 #include "state.h"
35 #include "mtypes.h"
36 #include "main/dispatch.h"
37 
38 
39 void GLAPIENTRY
_mesa_ClearAccum(GLfloat red,GLfloat green,GLfloat blue,GLfloat alpha)40 _mesa_ClearAccum( GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha )
41 {
42    GLfloat tmp[4];
43    GET_CURRENT_CONTEXT(ctx);
44 
45    tmp[0] = CLAMP( red,   -1.0F, 1.0F );
46    tmp[1] = CLAMP( green, -1.0F, 1.0F );
47    tmp[2] = CLAMP( blue,  -1.0F, 1.0F );
48    tmp[3] = CLAMP( alpha, -1.0F, 1.0F );
49 
50    if (TEST_EQ_4V(tmp, ctx->Accum.ClearColor))
51       return;
52 
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    ctx->Driver.MapRenderbuffer(ctx, accRb, x, y, width, height,
86                                GL_MAP_WRITE_BIT, &accMap, &accRowStride);
87 
88    if (!accMap) {
89       _mesa_error(ctx, GL_OUT_OF_MEMORY, "glAccum");
90       return;
91    }
92 
93    if (accRb->Format == MESA_FORMAT_RGBA_SNORM16) {
94       const GLshort clearR = FLOAT_TO_SHORT(ctx->Accum.ClearColor[0]);
95       const GLshort clearG = FLOAT_TO_SHORT(ctx->Accum.ClearColor[1]);
96       const GLshort clearB = FLOAT_TO_SHORT(ctx->Accum.ClearColor[2]);
97       const GLshort clearA = FLOAT_TO_SHORT(ctx->Accum.ClearColor[3]);
98       GLuint i, j;
99 
100       for (j = 0; j < height; j++) {
101          GLshort *row = (GLshort *) accMap;
102 
103          for (i = 0; i < width; i++) {
104             row[i * 4 + 0] = clearR;
105             row[i * 4 + 1] = clearG;
106             row[i * 4 + 2] = clearB;
107             row[i * 4 + 3] = clearA;
108          }
109          accMap += accRowStride;
110       }
111    }
112    else {
113       /* other types someday? */
114       _mesa_warning(ctx, "unexpected accum buffer type");
115    }
116 
117    ctx->Driver.UnmapRenderbuffer(ctx, accRb);
118 }
119 
120 
121 /**
122  * if (bias)
123  *    Accum += value
124  * else
125  *    Accum *= value
126  */
127 static void
accum_scale_or_bias(struct gl_context * ctx,GLfloat value,GLint xpos,GLint ypos,GLint width,GLint height,GLboolean bias)128 accum_scale_or_bias(struct gl_context *ctx, GLfloat value,
129                     GLint xpos, GLint ypos, GLint width, GLint height,
130                     GLboolean bias)
131 {
132    struct gl_renderbuffer *accRb =
133       ctx->DrawBuffer->Attachment[BUFFER_ACCUM].Renderbuffer;
134    GLubyte *accMap;
135    GLint accRowStride;
136 
137    assert(accRb);
138 
139    ctx->Driver.MapRenderbuffer(ctx, accRb, xpos, ypos, width, height,
140                                GL_MAP_READ_BIT | GL_MAP_WRITE_BIT,
141                                &accMap, &accRowStride);
142 
143    if (!accMap) {
144       _mesa_error(ctx, GL_OUT_OF_MEMORY, "glAccum");
145       return;
146    }
147 
148    if (accRb->Format == MESA_FORMAT_RGBA_SNORM16) {
149       const GLshort incr = (GLshort) (value * 32767.0f);
150       GLint i, j;
151       if (bias) {
152          for (j = 0; j < height; j++) {
153             GLshort *acc = (GLshort *) accMap;
154             for (i = 0; i < 4 * width; i++) {
155                acc[i] += incr;
156             }
157             accMap += accRowStride;
158          }
159       }
160       else {
161          /* scale */
162          for (j = 0; j < height; j++) {
163             GLshort *acc = (GLshort *) accMap;
164             for (i = 0; i < 4 * width; i++) {
165                acc[i] = (GLshort) (acc[i] * value);
166             }
167             accMap += accRowStride;
168          }
169       }
170    }
171    else {
172       /* other types someday? */
173    }
174 
175    ctx->Driver.UnmapRenderbuffer(ctx, accRb);
176 }
177 
178 
179 /**
180  * if (load)
181  *    Accum = ColorBuf * value
182  * else
183  *    Accum += ColorBuf * value
184  */
185 static void
accum_or_load(struct gl_context * ctx,GLfloat value,GLint xpos,GLint ypos,GLint width,GLint height,GLboolean load)186 accum_or_load(struct gl_context *ctx, GLfloat value,
187               GLint xpos, GLint ypos, GLint width, GLint height,
188               GLboolean load)
189 {
190    struct gl_renderbuffer *accRb =
191       ctx->DrawBuffer->Attachment[BUFFER_ACCUM].Renderbuffer;
192    struct gl_renderbuffer *colorRb = ctx->ReadBuffer->_ColorReadBuffer;
193    GLubyte *accMap, *colorMap;
194    GLint accRowStride, colorRowStride;
195    GLbitfield mappingFlags;
196 
197    if (!colorRb) {
198       /* no read buffer - OK */
199       return;
200    }
201 
202    assert(accRb);
203 
204    mappingFlags = GL_MAP_WRITE_BIT;
205    if (!load) /* if we're accumulating */
206       mappingFlags |= GL_MAP_READ_BIT;
207 
208    /* Map accum buffer */
209    ctx->Driver.MapRenderbuffer(ctx, accRb, xpos, ypos, width, height,
210                                mappingFlags, &accMap, &accRowStride);
211    if (!accMap) {
212       _mesa_error(ctx, GL_OUT_OF_MEMORY, "glAccum");
213       return;
214    }
215 
216    /* Map color buffer */
217    ctx->Driver.MapRenderbuffer(ctx, colorRb, xpos, ypos, width, height,
218                                GL_MAP_READ_BIT,
219                                &colorMap, &colorRowStride);
220    if (!colorMap) {
221       ctx->Driver.UnmapRenderbuffer(ctx, accRb);
222       _mesa_error(ctx, GL_OUT_OF_MEMORY, "glAccum");
223       return;
224    }
225 
226    if (accRb->Format == MESA_FORMAT_RGBA_SNORM16) {
227       const GLfloat scale = value * 32767.0f;
228       GLint i, j;
229       GLfloat (*rgba)[4];
230 
231       rgba = malloc(width * 4 * sizeof(GLfloat));
232       if (rgba) {
233          for (j = 0; j < height; j++) {
234             GLshort *acc = (GLshort *) accMap;
235 
236             /* read colors from source color buffer */
237             _mesa_unpack_rgba_row(colorRb->Format, width, colorMap, rgba);
238 
239             if (load) {
240                for (i = 0; i < width; i++) {
241                   acc[i * 4 + 0] = (GLshort) (rgba[i][RCOMP] * scale);
242                   acc[i * 4 + 1] = (GLshort) (rgba[i][GCOMP] * scale);
243                   acc[i * 4 + 2] = (GLshort) (rgba[i][BCOMP] * scale);
244                   acc[i * 4 + 3] = (GLshort) (rgba[i][ACOMP] * scale);
245                }
246             }
247             else {
248                /* accumulate */
249                for (i = 0; i < width; i++) {
250                   acc[i * 4 + 0] += (GLshort) (rgba[i][RCOMP] * scale);
251                   acc[i * 4 + 1] += (GLshort) (rgba[i][GCOMP] * scale);
252                   acc[i * 4 + 2] += (GLshort) (rgba[i][BCOMP] * scale);
253                   acc[i * 4 + 3] += (GLshort) (rgba[i][ACOMP] * scale);
254                }
255             }
256 
257             colorMap += colorRowStride;
258             accMap += accRowStride;
259          }
260 
261          free(rgba);
262       }
263       else {
264          _mesa_error(ctx, GL_OUT_OF_MEMORY, "glAccum");
265       }
266    }
267    else {
268       /* other types someday? */
269    }
270 
271    ctx->Driver.UnmapRenderbuffer(ctx, accRb);
272    ctx->Driver.UnmapRenderbuffer(ctx, colorRb);
273 }
274 
275 
276 /**
277  * ColorBuffer = Accum * value
278  */
279 static void
accum_return(struct gl_context * ctx,GLfloat value,GLint xpos,GLint ypos,GLint width,GLint height)280 accum_return(struct gl_context *ctx, GLfloat value,
281              GLint xpos, GLint ypos, GLint width, GLint height)
282 {
283    struct gl_framebuffer *fb = ctx->DrawBuffer;
284    struct gl_renderbuffer *accRb = fb->Attachment[BUFFER_ACCUM].Renderbuffer;
285    GLubyte *accMap, *colorMap;
286    GLint accRowStride, colorRowStride;
287    GLuint buffer;
288 
289    /* Map accum buffer */
290    ctx->Driver.MapRenderbuffer(ctx, accRb, xpos, ypos, width, height,
291                                GL_MAP_READ_BIT,
292                                &accMap, &accRowStride);
293    if (!accMap) {
294       _mesa_error(ctx, GL_OUT_OF_MEMORY, "glAccum");
295       return;
296    }
297 
298    /* Loop over destination buffers */
299    for (buffer = 0; buffer < fb->_NumColorDrawBuffers; buffer++) {
300       struct gl_renderbuffer *colorRb = fb->_ColorDrawBuffers[buffer];
301       const GLboolean masking = (!ctx->Color.ColorMask[buffer][RCOMP] ||
302                                  !ctx->Color.ColorMask[buffer][GCOMP] ||
303                                  !ctx->Color.ColorMask[buffer][BCOMP] ||
304                                  !ctx->Color.ColorMask[buffer][ACOMP]);
305       GLbitfield mappingFlags = GL_MAP_WRITE_BIT;
306 
307       if (masking)
308          mappingFlags |= GL_MAP_READ_BIT;
309 
310       /* Map color buffer */
311       ctx->Driver.MapRenderbuffer(ctx, colorRb, xpos, ypos, width, height,
312                                   mappingFlags, &colorMap, &colorRowStride);
313       if (!colorMap) {
314          _mesa_error(ctx, GL_OUT_OF_MEMORY, "glAccum");
315          continue;
316       }
317 
318       if (accRb->Format == MESA_FORMAT_RGBA_SNORM16) {
319          const GLfloat scale = value / 32767.0f;
320          GLint i, j;
321          GLfloat (*rgba)[4], (*dest)[4];
322 
323          rgba = malloc(width * 4 * sizeof(GLfloat));
324          dest = malloc(width * 4 * sizeof(GLfloat));
325 
326          if (rgba && dest) {
327             for (j = 0; j < height; j++) {
328                GLshort *acc = (GLshort *) accMap;
329 
330                for (i = 0; i < width; i++) {
331                   rgba[i][0] = acc[i * 4 + 0] * scale;
332                   rgba[i][1] = acc[i * 4 + 1] * scale;
333                   rgba[i][2] = acc[i * 4 + 2] * scale;
334                   rgba[i][3] = acc[i * 4 + 3] * scale;
335                }
336 
337                if (masking) {
338 
339                   /* get existing colors from dest buffer */
340                   _mesa_unpack_rgba_row(colorRb->Format, width, colorMap, dest);
341 
342                   /* use the dest colors where mask[channel] = 0 */
343                   if (ctx->Color.ColorMask[buffer][RCOMP] == 0) {
344                      for (i = 0; i < width; i++)
345                         rgba[i][RCOMP] = dest[i][RCOMP];
346                   }
347                   if (ctx->Color.ColorMask[buffer][GCOMP] == 0) {
348                      for (i = 0; i < width; i++)
349                         rgba[i][GCOMP] = dest[i][GCOMP];
350                   }
351                   if (ctx->Color.ColorMask[buffer][BCOMP] == 0) {
352                      for (i = 0; i < width; i++)
353                         rgba[i][BCOMP] = dest[i][BCOMP];
354                   }
355                   if (ctx->Color.ColorMask[buffer][ACOMP] == 0) {
356                      for (i = 0; i < width; i++)
357                         rgba[i][ACOMP] = dest[i][ACOMP];
358                   }
359                }
360 
361                _mesa_pack_float_rgba_row(colorRb->Format, width,
362                                          (const GLfloat (*)[4]) rgba, colorMap);
363 
364                accMap += accRowStride;
365                colorMap += colorRowStride;
366             }
367          }
368          else {
369             _mesa_error(ctx, GL_OUT_OF_MEMORY, "glAccum");
370          }
371          free(rgba);
372          free(dest);
373       }
374       else {
375          /* other types someday? */
376       }
377 
378       ctx->Driver.UnmapRenderbuffer(ctx, colorRb);
379    }
380 
381    ctx->Driver.UnmapRenderbuffer(ctx, accRb);
382 }
383 
384 
385 
386 /**
387  * Software fallback for glAccum.  A hardware driver that supports
388  * signed 16-bit color channels could implement hardware accumulation
389  * operations, but no driver does so at this time.
390  */
391 static void
accum(struct gl_context * ctx,GLenum op,GLfloat value)392 accum(struct gl_context *ctx, GLenum op, GLfloat value)
393 {
394    GLint xpos, ypos, width, height;
395 
396    if (!ctx->DrawBuffer->Attachment[BUFFER_ACCUM].Renderbuffer) {
397       _mesa_warning(ctx, "Calling glAccum() without an accumulation buffer");
398       return;
399    }
400 
401    if (!_mesa_check_conditional_render(ctx))
402       return;
403 
404    _mesa_update_draw_buffer_bounds(ctx, ctx->DrawBuffer);
405 
406    xpos = ctx->DrawBuffer->_Xmin;
407    ypos = ctx->DrawBuffer->_Ymin;
408    width =  ctx->DrawBuffer->_Xmax - ctx->DrawBuffer->_Xmin;
409    height = ctx->DrawBuffer->_Ymax - ctx->DrawBuffer->_Ymin;
410 
411    switch (op) {
412    case GL_ADD:
413       if (value != 0.0F) {
414          accum_scale_or_bias(ctx, value, xpos, ypos, width, height, GL_TRUE);
415       }
416       break;
417    case GL_MULT:
418       if (value != 1.0F) {
419          accum_scale_or_bias(ctx, value, xpos, ypos, width, height, GL_FALSE);
420       }
421       break;
422    case GL_ACCUM:
423       if (value != 0.0F) {
424          accum_or_load(ctx, value, xpos, ypos, width, height, GL_FALSE);
425       }
426       break;
427    case GL_LOAD:
428       accum_or_load(ctx, value, xpos, ypos, width, height, GL_TRUE);
429       break;
430    case GL_RETURN:
431       accum_return(ctx, value, xpos, ypos, width, height);
432       break;
433    default:
434       unreachable("invalid mode in _mesa_Accum()");
435       break;
436    }
437 }
438 
439 
440 void
_mesa_init_accum(struct gl_context * ctx)441 _mesa_init_accum( struct gl_context *ctx )
442 {
443    /* Accumulate buffer group */
444    ASSIGN_4V( ctx->Accum.ClearColor, 0.0, 0.0, 0.0, 0.0 );
445 }
446 
447 
448 void GLAPIENTRY
_mesa_Accum(GLenum op,GLfloat value)449 _mesa_Accum( GLenum op, GLfloat value )
450 {
451    GET_CURRENT_CONTEXT(ctx);
452    FLUSH_VERTICES(ctx, 0);
453 
454    switch (op) {
455    case GL_ADD:
456    case GL_MULT:
457    case GL_ACCUM:
458    case GL_LOAD:
459    case GL_RETURN:
460       /* OK */
461       break;
462    default:
463       _mesa_error(ctx, GL_INVALID_ENUM, "glAccum(op)");
464       return;
465    }
466 
467    if (ctx->DrawBuffer->Visual.haveAccumBuffer == 0) {
468       _mesa_error(ctx, GL_INVALID_OPERATION, "glAccum(no accum buffer)");
469       return;
470    }
471 
472    if (ctx->DrawBuffer != ctx->ReadBuffer) {
473       /* See GLX_SGI_make_current_read or WGL_ARB_make_current_read,
474        * or GL_EXT_framebuffer_blit.
475        */
476       _mesa_error(ctx, GL_INVALID_OPERATION,
477                   "glAccum(different read/draw buffers)");
478       return;
479    }
480 
481    if (ctx->NewState)
482       _mesa_update_state(ctx);
483 
484    if (ctx->DrawBuffer->_Status != GL_FRAMEBUFFER_COMPLETE_EXT) {
485       _mesa_error(ctx, GL_INVALID_FRAMEBUFFER_OPERATION_EXT,
486                   "glAccum(incomplete framebuffer)");
487       return;
488    }
489 
490    if (ctx->RasterDiscard)
491       return;
492 
493    if (ctx->RenderMode == GL_RENDER) {
494       accum(ctx, op, value);
495    }
496 }
497