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