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