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 38void GLAPIENTRY 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 */ 62void 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 */ 128static void 129accum_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 */ 187static void 188accum_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 */ 283static void 284accum_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 */ 396static void 397accum(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 445void 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 453void GLAPIENTRY 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