1 /* 2 * Mesa 3-D graphics library 3 * 4 * Copyright (C) 1999-2007 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 "main/framebuffer.h" 26 #include "main/glheader.h" 27 #include "main/macros.h" 28 #include "s_context.h" 29 #include "s_feedback.h" 30 #include "s_points.h" 31 #include "s_span.h" 32 33 34 /** 35 * Used to cull points with invalid coords 36 */ 37 #define CULL_INVALID(V) \ 38 do { \ 39 float tmp = (V)->attrib[VARYING_SLOT_POS][0] \ 40 + (V)->attrib[VARYING_SLOT_POS][1]; \ 41 if (IS_INF_OR_NAN(tmp)) \ 42 return; \ 43 } while(0) 44 45 46 47 /** 48 * Get/compute the point size. 49 * The size may come from a vertex shader, or computed with attentuation 50 * or just the glPointSize value. 51 * Must also clamp to user-defined range and implmentation limits. 52 */ 53 static inline GLfloat 54 get_size(const struct gl_context *ctx, const SWvertex *vert, GLboolean smoothed) 55 { 56 GLfloat size; 57 58 if (ctx->Point._Attenuated || ctx->VertexProgram.PointSizeEnabled) { 59 /* use vertex's point size */ 60 size = vert->pointSize; 61 } 62 else { 63 /* use constant point size */ 64 size = ctx->Point.Size; 65 } 66 /* always clamp to user-specified limits */ 67 size = CLAMP(size, ctx->Point.MinSize, ctx->Point.MaxSize); 68 /* clamp to implementation limits */ 69 if (smoothed) 70 size = CLAMP(size, ctx->Const.MinPointSizeAA, ctx->Const.MaxPointSizeAA); 71 else 72 size = CLAMP(size, ctx->Const.MinPointSize, ctx->Const.MaxPointSize); 73 74 return size; 75 } 76 77 78 /** 79 * Draw a point sprite 80 */ 81 static void 82 sprite_point(struct gl_context *ctx, const SWvertex *vert) 83 { 84 SWcontext *swrast = SWRAST_CONTEXT(ctx); 85 SWspan span; 86 GLfloat size; 87 GLuint tCoords[MAX_TEXTURE_COORD_UNITS + 1]; 88 GLuint numTcoords = 0; 89 GLfloat t0, dtdy; 90 91 CULL_INVALID(vert); 92 93 /* z coord */ 94 if (ctx->DrawBuffer->Visual.depthBits <= 16) 95 span.z = FloatToFixed(vert->attrib[VARYING_SLOT_POS][2] + 0.5F); 96 else 97 span.z = (GLuint) (vert->attrib[VARYING_SLOT_POS][2] + 0.5F); 98 span.zStep = 0; 99 100 size = get_size(ctx, vert, GL_FALSE); 101 102 /* span init */ 103 INIT_SPAN(span, GL_POINT); 104 span.interpMask = SPAN_Z | SPAN_RGBA; 105 106 span.facing = swrast->PointLineFacing; 107 108 span.red = ChanToFixed(vert->color[0]); 109 span.green = ChanToFixed(vert->color[1]); 110 span.blue = ChanToFixed(vert->color[2]); 111 span.alpha = ChanToFixed(vert->color[3]); 112 span.redStep = 0; 113 span.greenStep = 0; 114 span.blueStep = 0; 115 span.alphaStep = 0; 116 117 /* need these for fragment programs */ 118 span.attrStart[VARYING_SLOT_POS][3] = 1.0F; 119 span.attrStepX[VARYING_SLOT_POS][3] = 0.0F; 120 span.attrStepY[VARYING_SLOT_POS][3] = 0.0F; 121 122 { 123 GLfloat s, r, dsdx; 124 125 /* texcoord / pointcoord interpolants */ 126 s = 0.0F; 127 dsdx = 1.0F / size; 128 if (ctx->Point.SpriteOrigin == GL_LOWER_LEFT) { 129 dtdy = 1.0F / size; 130 t0 = 0.5F * dtdy; 131 } 132 else { 133 /* GL_UPPER_LEFT */ 134 dtdy = -1.0F / size; 135 t0 = 1.0F + 0.5F * dtdy; 136 } 137 138 ATTRIB_LOOP_BEGIN 139 if (attr >= VARYING_SLOT_TEX0 && attr <= VARYING_SLOT_TEX7) { 140 /* a texcoord attribute */ 141 const GLuint u = attr - VARYING_SLOT_TEX0; 142 assert(u < MAX_TEXTURE_COORD_UNITS); 143 if (ctx->Point.CoordReplace & (1u << u)) { 144 tCoords[numTcoords++] = attr; 145 146 if (ctx->Point.SpriteRMode == GL_ZERO) 147 r = 0.0F; 148 else if (ctx->Point.SpriteRMode == GL_S) 149 r = vert->attrib[attr][0]; 150 else /* GL_R */ 151 r = vert->attrib[attr][2]; 152 153 span.attrStart[attr][0] = s; 154 span.attrStart[attr][1] = 0.0; /* overwritten below */ 155 span.attrStart[attr][2] = r; 156 span.attrStart[attr][3] = 1.0; 157 158 span.attrStepX[attr][0] = dsdx; 159 span.attrStepX[attr][1] = 0.0; 160 span.attrStepX[attr][2] = 0.0; 161 span.attrStepX[attr][3] = 0.0; 162 163 span.attrStepY[attr][0] = 0.0; 164 span.attrStepY[attr][1] = dtdy; 165 span.attrStepY[attr][2] = 0.0; 166 span.attrStepY[attr][3] = 0.0; 167 168 continue; 169 } 170 } 171 else if (attr == VARYING_SLOT_PNTC) { 172 /* GLSL gl_PointCoord.xy (.zw undefined) */ 173 span.attrStart[VARYING_SLOT_PNTC][0] = 0.0; 174 span.attrStart[VARYING_SLOT_PNTC][1] = 0.0; /* t0 set below */ 175 span.attrStepX[VARYING_SLOT_PNTC][0] = dsdx; 176 span.attrStepX[VARYING_SLOT_PNTC][1] = 0.0; 177 span.attrStepY[VARYING_SLOT_PNTC][0] = 0.0; 178 span.attrStepY[VARYING_SLOT_PNTC][1] = dtdy; 179 tCoords[numTcoords++] = VARYING_SLOT_PNTC; 180 continue; 181 } 182 /* use vertex's texcoord/attrib */ 183 COPY_4V(span.attrStart[attr], vert->attrib[attr]); 184 ASSIGN_4V(span.attrStepX[attr], 0, 0, 0, 0); 185 ASSIGN_4V(span.attrStepY[attr], 0, 0, 0, 0); 186 ATTRIB_LOOP_END; 187 } 188 189 /* compute pos, bounds and render */ 190 { 191 const GLfloat x = vert->attrib[VARYING_SLOT_POS][0]; 192 const GLfloat y = vert->attrib[VARYING_SLOT_POS][1]; 193 GLint iSize = (GLint) (size + 0.5F); 194 GLint xmin, xmax, ymin, ymax, iy; 195 GLint iRadius; 196 GLfloat tcoord = t0; 197 198 iSize = MAX2(1, iSize); 199 iRadius = iSize / 2; 200 201 if (iSize & 1) { 202 /* odd size */ 203 xmin = (GLint) (x - iRadius); 204 xmax = (GLint) (x + iRadius); 205 ymin = (GLint) (y - iRadius); 206 ymax = (GLint) (y + iRadius); 207 } 208 else { 209 /* even size */ 210 /* 0.501 factor allows conformance to pass */ 211 xmin = (GLint) (x + 0.501F) - iRadius; 212 xmax = xmin + iSize - 1; 213 ymin = (GLint) (y + 0.501F) - iRadius; 214 ymax = ymin + iSize - 1; 215 } 216 217 /* render spans */ 218 for (iy = ymin; iy <= ymax; iy++) { 219 GLuint i; 220 /* setup texcoord T for this row */ 221 for (i = 0; i < numTcoords; i++) { 222 span.attrStart[tCoords[i]][1] = tcoord; 223 } 224 225 /* these might get changed by span clipping */ 226 span.x = xmin; 227 span.y = iy; 228 span.end = xmax - xmin + 1; 229 230 _swrast_write_rgba_span(ctx, &span); 231 232 tcoord += dtdy; 233 } 234 } 235 } 236 237 238 /** 239 * Draw smooth/antialiased point. RGB or CI mode. 240 */ 241 static void 242 smooth_point(struct gl_context *ctx, const SWvertex *vert) 243 { 244 SWcontext *swrast = SWRAST_CONTEXT(ctx); 245 SWspan span; 246 GLfloat size, alphaAtten; 247 248 CULL_INVALID(vert); 249 250 /* z coord */ 251 if (ctx->DrawBuffer->Visual.depthBits <= 16) 252 span.z = FloatToFixed(vert->attrib[VARYING_SLOT_POS][2] + 0.5F); 253 else 254 span.z = (GLuint) (vert->attrib[VARYING_SLOT_POS][2] + 0.5F); 255 span.zStep = 0; 256 257 size = get_size(ctx, vert, GL_TRUE); 258 259 /* alpha attenuation / fade factor */ 260 if (_mesa_is_multisample_enabled(ctx)) { 261 if (vert->pointSize >= ctx->Point.Threshold) { 262 alphaAtten = 1.0F; 263 } 264 else { 265 GLfloat dsize = vert->pointSize / ctx->Point.Threshold; 266 alphaAtten = dsize * dsize; 267 } 268 } 269 else { 270 alphaAtten = 1.0; 271 } 272 (void) alphaAtten; /* not used */ 273 274 /* span init */ 275 INIT_SPAN(span, GL_POINT); 276 span.interpMask = SPAN_Z | SPAN_RGBA; 277 span.arrayMask = SPAN_COVERAGE | SPAN_MASK; 278 279 span.facing = swrast->PointLineFacing; 280 281 span.red = ChanToFixed(vert->color[0]); 282 span.green = ChanToFixed(vert->color[1]); 283 span.blue = ChanToFixed(vert->color[2]); 284 span.alpha = ChanToFixed(vert->color[3]); 285 span.redStep = 0; 286 span.greenStep = 0; 287 span.blueStep = 0; 288 span.alphaStep = 0; 289 290 /* need these for fragment programs */ 291 span.attrStart[VARYING_SLOT_POS][3] = 1.0F; 292 span.attrStepX[VARYING_SLOT_POS][3] = 0.0F; 293 span.attrStepY[VARYING_SLOT_POS][3] = 0.0F; 294 295 ATTRIB_LOOP_BEGIN 296 COPY_4V(span.attrStart[attr], vert->attrib[attr]); 297 ASSIGN_4V(span.attrStepX[attr], 0, 0, 0, 0); 298 ASSIGN_4V(span.attrStepY[attr], 0, 0, 0, 0); 299 ATTRIB_LOOP_END 300 301 /* compute pos, bounds and render */ 302 { 303 const GLfloat x = vert->attrib[VARYING_SLOT_POS][0]; 304 const GLfloat y = vert->attrib[VARYING_SLOT_POS][1]; 305 const GLfloat radius = 0.5F * size; 306 const GLfloat rmin = radius - 0.7071F; /* 0.7071 = sqrt(2)/2 */ 307 const GLfloat rmax = radius + 0.7071F; 308 const GLfloat rmin2 = MAX2(0.0F, rmin * rmin); 309 const GLfloat rmax2 = rmax * rmax; 310 const GLfloat cscale = 1.0F / (rmax2 - rmin2); 311 const GLint xmin = (GLint) (x - radius); 312 const GLint xmax = (GLint) (x + radius); 313 const GLint ymin = (GLint) (y - radius); 314 const GLint ymax = (GLint) (y + radius); 315 GLint ix, iy; 316 317 for (iy = ymin; iy <= ymax; iy++) { 318 319 /* these might get changed by span clipping */ 320 span.x = xmin; 321 span.y = iy; 322 span.end = xmax - xmin + 1; 323 324 /* compute coverage for each pixel in span */ 325 for (ix = xmin; ix <= xmax; ix++) { 326 const GLfloat dx = ix - x + 0.5F; 327 const GLfloat dy = iy - y + 0.5F; 328 const GLfloat dist2 = dx * dx + dy * dy; 329 GLfloat coverage; 330 331 if (dist2 < rmax2) { 332 if (dist2 >= rmin2) { 333 /* compute partial coverage */ 334 coverage = 1.0F - (dist2 - rmin2) * cscale; 335 } 336 else { 337 /* full coverage */ 338 coverage = 1.0F; 339 } 340 span.array->mask[ix - xmin] = 1; 341 } 342 else { 343 /* zero coverage - fragment outside the radius */ 344 coverage = 0.0; 345 span.array->mask[ix - xmin] = 0; 346 } 347 span.array->coverage[ix - xmin] = coverage; 348 } 349 350 /* render span */ 351 _swrast_write_rgba_span(ctx, &span); 352 353 } 354 } 355 } 356 357 358 /** 359 * Draw large (size >= 1) non-AA point. RGB or CI mode. 360 */ 361 static void 362 large_point(struct gl_context *ctx, const SWvertex *vert) 363 { 364 SWcontext *swrast = SWRAST_CONTEXT(ctx); 365 SWspan span; 366 GLfloat size; 367 368 CULL_INVALID(vert); 369 370 /* z coord */ 371 if (ctx->DrawBuffer->Visual.depthBits <= 16) 372 span.z = FloatToFixed(vert->attrib[VARYING_SLOT_POS][2] + 0.5F); 373 else 374 span.z = (GLuint) (vert->attrib[VARYING_SLOT_POS][2] + 0.5F); 375 span.zStep = 0; 376 377 size = get_size(ctx, vert, GL_FALSE); 378 379 /* span init */ 380 INIT_SPAN(span, GL_POINT); 381 span.arrayMask = SPAN_XY; 382 span.facing = swrast->PointLineFacing; 383 384 span.interpMask = SPAN_Z | SPAN_RGBA; 385 span.red = ChanToFixed(vert->color[0]); 386 span.green = ChanToFixed(vert->color[1]); 387 span.blue = ChanToFixed(vert->color[2]); 388 span.alpha = ChanToFixed(vert->color[3]); 389 span.redStep = 0; 390 span.greenStep = 0; 391 span.blueStep = 0; 392 span.alphaStep = 0; 393 394 /* need these for fragment programs */ 395 span.attrStart[VARYING_SLOT_POS][3] = 1.0F; 396 span.attrStepX[VARYING_SLOT_POS][3] = 0.0F; 397 span.attrStepY[VARYING_SLOT_POS][3] = 0.0F; 398 399 ATTRIB_LOOP_BEGIN 400 COPY_4V(span.attrStart[attr], vert->attrib[attr]); 401 ASSIGN_4V(span.attrStepX[attr], 0, 0, 0, 0); 402 ASSIGN_4V(span.attrStepY[attr], 0, 0, 0, 0); 403 ATTRIB_LOOP_END 404 405 /* compute pos, bounds and render */ 406 { 407 const GLfloat x = vert->attrib[VARYING_SLOT_POS][0]; 408 const GLfloat y = vert->attrib[VARYING_SLOT_POS][1]; 409 GLint iSize = (GLint) (size + 0.5F); 410 GLint xmin, xmax, ymin, ymax, ix, iy; 411 GLint iRadius; 412 413 iSize = MAX2(1, iSize); 414 iRadius = iSize / 2; 415 416 if (iSize & 1) { 417 /* odd size */ 418 xmin = (GLint) (x - iRadius); 419 xmax = (GLint) (x + iRadius); 420 ymin = (GLint) (y - iRadius); 421 ymax = (GLint) (y + iRadius); 422 } 423 else { 424 /* even size */ 425 /* 0.501 factor allows conformance to pass */ 426 xmin = (GLint) (x + 0.501F) - iRadius; 427 xmax = xmin + iSize - 1; 428 ymin = (GLint) (y + 0.501F) - iRadius; 429 ymax = ymin + iSize - 1; 430 } 431 432 /* generate fragments */ 433 span.end = 0; 434 for (iy = ymin; iy <= ymax; iy++) { 435 for (ix = xmin; ix <= xmax; ix++) { 436 span.array->x[span.end] = ix; 437 span.array->y[span.end] = iy; 438 span.end++; 439 } 440 } 441 assert(span.end <= SWRAST_MAX_WIDTH); 442 _swrast_write_rgba_span(ctx, &span); 443 } 444 } 445 446 447 /** 448 * Draw size=1, single-pixel point 449 */ 450 static void 451 pixel_point(struct gl_context *ctx, const SWvertex *vert) 452 { 453 SWcontext *swrast = SWRAST_CONTEXT(ctx); 454 /* 455 * Note that unlike the other functions, we put single-pixel points 456 * into a special span array in order to render as many points as 457 * possible with a single _swrast_write_rgba_span() call. 458 */ 459 SWspan *span = &(swrast->PointSpan); 460 GLuint count; 461 462 CULL_INVALID(vert); 463 464 /* Span init */ 465 span->interpMask = 0; 466 span->arrayMask = SPAN_XY | SPAN_Z; 467 span->arrayMask |= SPAN_RGBA; 468 /*span->arrayMask |= SPAN_LAMBDA;*/ 469 span->arrayAttribs = swrast->_ActiveAttribMask; /* we'll produce these vals */ 470 471 /* need these for fragment programs */ 472 span->attrStart[VARYING_SLOT_POS][3] = 1.0F; 473 span->attrStepX[VARYING_SLOT_POS][3] = 0.0F; 474 span->attrStepY[VARYING_SLOT_POS][3] = 0.0F; 475 476 /* check if we need to flush */ 477 if (span->end >= SWRAST_MAX_WIDTH || 478 (swrast->_RasterMask & (BLEND_BIT | LOGIC_OP_BIT | MASKING_BIT)) || 479 span->facing != swrast->PointLineFacing) { 480 if (span->end > 0) { 481 _swrast_write_rgba_span(ctx, span); 482 span->end = 0; 483 } 484 } 485 486 count = span->end; 487 488 span->facing = swrast->PointLineFacing; 489 490 /* fragment attributes */ 491 span->array->rgba[count][RCOMP] = vert->color[0]; 492 span->array->rgba[count][GCOMP] = vert->color[1]; 493 span->array->rgba[count][BCOMP] = vert->color[2]; 494 span->array->rgba[count][ACOMP] = vert->color[3]; 495 496 ATTRIB_LOOP_BEGIN 497 COPY_4V(span->array->attribs[attr][count], vert->attrib[attr]); 498 ATTRIB_LOOP_END 499 500 /* fragment position */ 501 span->array->x[count] = (GLint) vert->attrib[VARYING_SLOT_POS][0]; 502 span->array->y[count] = (GLint) vert->attrib[VARYING_SLOT_POS][1]; 503 span->array->z[count] = (GLint) (vert->attrib[VARYING_SLOT_POS][2] + 0.5F); 504 505 span->end = count + 1; 506 assert(span->end <= SWRAST_MAX_WIDTH); 507 } 508 509 510 /** 511 * Add specular color to primary color, draw point, restore original 512 * primary color. 513 */ 514 void 515 _swrast_add_spec_terms_point(struct gl_context *ctx, const SWvertex *v0) 516 { 517 SWvertex *ncv0 = (SWvertex *) v0; /* cast away const */ 518 GLfloat rSum, gSum, bSum; 519 GLchan cSave[4]; 520 521 /* save */ 522 COPY_CHAN4(cSave, ncv0->color); 523 /* sum */ 524 rSum = CHAN_TO_FLOAT(ncv0->color[0]) + ncv0->attrib[VARYING_SLOT_COL1][0]; 525 gSum = CHAN_TO_FLOAT(ncv0->color[1]) + ncv0->attrib[VARYING_SLOT_COL1][1]; 526 bSum = CHAN_TO_FLOAT(ncv0->color[2]) + ncv0->attrib[VARYING_SLOT_COL1][2]; 527 UNCLAMPED_FLOAT_TO_CHAN(ncv0->color[0], rSum); 528 UNCLAMPED_FLOAT_TO_CHAN(ncv0->color[1], gSum); 529 UNCLAMPED_FLOAT_TO_CHAN(ncv0->color[2], bSum); 530 /* draw */ 531 SWRAST_CONTEXT(ctx)->SpecPoint(ctx, ncv0); 532 /* restore */ 533 COPY_CHAN4(ncv0->color, cSave); 534 } 535 536 537 /** 538 * Examine current state to determine which point drawing function to use. 539 */ 540 void 541 _swrast_choose_point(struct gl_context *ctx) 542 { 543 SWcontext *swrast = SWRAST_CONTEXT(ctx); 544 const GLfloat size = CLAMP(ctx->Point.Size, 545 ctx->Point.MinSize, 546 ctx->Point.MaxSize); 547 548 if (ctx->RenderMode == GL_RENDER) { 549 if (ctx->Point.PointSprite) { 550 swrast->Point = sprite_point; 551 } 552 else if (ctx->Point.SmoothFlag) { 553 swrast->Point = smooth_point; 554 } 555 else if (size > 1.0F || 556 ctx->Point._Attenuated || 557 ctx->VertexProgram.PointSizeEnabled) { 558 swrast->Point = large_point; 559 } 560 else { 561 swrast->Point = pixel_point; 562 } 563 } 564 else if (ctx->RenderMode == GL_FEEDBACK) { 565 swrast->Point = _swrast_feedback_point; 566 } 567 else { 568 /* GL_SELECT mode */ 569 swrast->Point = _swrast_select_point; 570 } 571 } 572