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