1/* 2 Copyright (c) Microsoft Corporation 3 4 Permission is hereby granted, free of charge, to any person obtaining a copy 5 of this software and associated documentation files (the "Software"), to deal 6 in the Software without restriction, including without limitation the rights 7 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 copies of the Software, and to permit persons to whom the Software is 9 furnished to do so, subject to the following conditions: 10 11 The above copyright notice and this permission notice shall be included in 12 all copies or substantial portions of the Software. 13 14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 SOFTWARE. 21*/ 22 23#include "geometry.h" 24#include "tessellator.h" 25#include <agx_pack.h> 26 27#if 0 28#include <math.h> 29#include <stdbool.h> 30#include <stdint.h> 31#include <string.h> 32#include "util/macros.h" 33#define min(x, y) (x < y ? x : y) 34#define max(x, y) (x > y ? x : y) 35#define clz(x) (x ? __builtin_clz(x) : (8 * sizeof(x))) 36#define clamp(x, y, z) (x < y ? y : x > z ? z : x) 37#define align(x, y) ALIGN_POT(x, y) 38#else 39#define assert(x) 40#endif 41 42#define LIBAGX_TESS_MIN_ISOLINE_DENSITY_TESSELLATION_FACTOR 1.0f 43#define LIBAGX_TESS_MAX_ISOLINE_DENSITY_TESSELLATION_FACTOR 64.0f 44 45typedef unsigned int FXP; // fixed point number 46 47enum { 48 U = 0, // points on a tri patch 49 V = 1, 50}; 51 52enum { 53 Ueq0 = 0, // edges on a tri patch 54 Veq0 = 1, 55 Weq0 = 2, 56}; 57 58enum { 59 Ueq1 = 2, // edges on a quad patch: Ueq0, Veq0, Ueq1, Veq1 60 Veq1 = 3, 61}; 62 63#define QUAD_AXES 2 64#define QUAD_EDGES 4 65#define TRI_EDGES 3 66 67// The interior can just use a simpler stitch. 68typedef enum DIAGONALS { 69 DIAGONALS_INSIDE_TO_OUTSIDE, 70 DIAGONALS_INSIDE_TO_OUTSIDE_EXCEPT_MIDDLE, 71 DIAGONALS_MIRRORED 72} DIAGONALS; 73 74typedef struct TESS_FACTOR_CONTEXT { 75 FXP fxpInvNumSegmentsOnFloorTessFactor; 76 FXP fxpInvNumSegmentsOnCeilTessFactor; 77 FXP fxpHalfTessFactorFraction; 78 int numHalfTessFactorPoints; 79 int splitPointOnFloorHalfTessFactor; 80} TESS_FACTOR_CONTEXT; 81 82struct INDEX_PATCH_CONTEXT { 83 int insidePointIndexDeltaToRealValue; 84 int insidePointIndexBadValue; 85 int insidePointIndexReplacementValue; 86 int outsidePointIndexPatchBase; 87 int outsidePointIndexDeltaToRealValue; 88 int outsidePointIndexBadValue; 89 int outsidePointIndexReplacementValue; 90}; 91 92struct INDEX_PATCH_CONTEXT2 { 93 int baseIndexToInvert; 94 int indexInversionEndPoint; 95 int cornerCaseBadValue; 96 int cornerCaseReplacementValue; 97}; 98 99struct CHWTessellator { 100 enum libagx_tess_output_primitive outputPrimitive; 101 enum libagx_tess_mode mode; 102 uint index_bias; 103 104 // array where we will store u/v's for the points we generate 105 global struct libagx_tess_point *Point; 106 107 // array where we will store index topology 108 global void *Index; 109 110 // A second index patch we have to do handles the leftover strip of quads in 111 // the middle of an odd quad patch after finishing all the concentric rings. 112 // This also handles the leftover strip of points in the middle of an even 113 // quad patch, when stitching the row of triangles up the left side (V major 114 // quad) or bottom (U major quad) of the inner ring 115 bool bUsingPatchedIndices; 116 bool bUsingPatchedIndices2; 117 struct INDEX_PATCH_CONTEXT IndexPatchCtx; 118 struct INDEX_PATCH_CONTEXT2 IndexPatchCtx2; 119}; 120 121#define FXP_INTEGER_BITS 15 122#define FXP_FRACTION_BITS 16 123#define FXP_FRACTION_MASK 0x0000ffff 124#define FXP_INTEGER_MASK 0x7fff0000 125#define FXP_ONE (1 << FXP_FRACTION_BITS) 126#define FXP_ONE_THIRD 0x00005555 127#define FXP_TWO_THIRDS 0x0000aaaa 128#define FXP_ONE_HALF 0x00008000 129 130static global float * 131tess_factors(constant struct libagx_tess_args *p, uint patch) 132{ 133 return p->tcs_buffer + (patch * p->tcs_stride_el); 134} 135 136static inline uint 137libagx_heap_alloc(global struct agx_geometry_state *heap, uint size_B) 138{ 139 // TODO: drop align to 4 I think 140 return atomic_fetch_add((volatile atomic_uint *)(&heap->heap_bottom), 141 align(size_B, 8)); 142} 143 144/* 145 * Generate an indexed draw for a patch with the computed number of indices. 146 * This allocates heap memory for the index buffer, returning the allocated 147 * memory. 148 */ 149static global void * 150libagx_draw(constant struct libagx_tess_args *p, enum libagx_tess_mode mode, 151 bool lines, uint patch, uint count) 152{ 153 if (mode == LIBAGX_TESS_MODE_COUNT) { 154 p->counts[patch] = count; 155 } 156 157 if (mode == LIBAGX_TESS_MODE_VDM) { 158 uint32_t elsize_B = sizeof(uint16_t); 159 uint32_t alloc_B = libagx_heap_alloc(p->heap, elsize_B * count); 160 uint64_t ib = ((uintptr_t)p->heap->heap) + alloc_B; 161 162 global uint32_t *desc = p->out_draws + (patch * 6); 163 agx_pack(&desc[0], INDEX_LIST, cfg) { 164 cfg.index_buffer_hi = (ib >> 32); 165 cfg.primitive = lines ? AGX_PRIMITIVE_LINES : AGX_PRIMITIVE_TRIANGLES; 166 cfg.restart_enable = false; 167 cfg.index_size = AGX_INDEX_SIZE_U16; 168 cfg.index_buffer_size_present = true; 169 cfg.index_buffer_present = true; 170 cfg.index_count_present = true; 171 cfg.instance_count_present = true; 172 cfg.start_present = true; 173 cfg.unk_1_present = false; 174 cfg.indirect_buffer_present = false; 175 cfg.unk_2_present = false; 176 cfg.block_type = AGX_VDM_BLOCK_TYPE_INDEX_LIST; 177 } 178 179 agx_pack(&desc[1], INDEX_LIST_BUFFER_LO, cfg) { 180 cfg.buffer_lo = ib & 0xffffffff; 181 } 182 183 agx_pack(&desc[2], INDEX_LIST_COUNT, cfg) { 184 cfg.count = count; 185 } 186 187 agx_pack(&desc[3], INDEX_LIST_INSTANCES, cfg) { 188 cfg.count = 1; 189 } 190 191 agx_pack(&desc[4], INDEX_LIST_START, cfg) { 192 cfg.start = patch * LIBAGX_TES_PATCH_ID_STRIDE; 193 } 194 195 agx_pack(&desc[5], INDEX_LIST_BUFFER_SIZE, cfg) { 196 cfg.size = align(count * 2, 4); 197 } 198 199 return (global void *)ib; 200 } 201 202 if (mode == LIBAGX_TESS_MODE_WITH_COUNTS) { 203 /* The index buffer is already allocated, get a pointer inside it. 204 * p->counts has had an inclusive prefix sum hence the subtraction. 205 */ 206 uint offset_el = p->counts[sub_sat(patch, 1u)]; 207 if (patch == 0) 208 offset_el = 0; 209 210 return &p->index_buffer[offset_el]; 211 } 212 213 return NULL; 214} 215 216static void 217libagx_draw_points(private struct CHWTessellator *ctx, 218 constant struct libagx_tess_args *p, uint patch, uint count) 219{ 220 if (ctx->mode == LIBAGX_TESS_MODE_VDM) { 221 /* Generate a non-indexed draw for points mode tessellation. */ 222 global uint32_t *desc = p->out_draws + (patch * 4); 223 agx_pack(&desc[0], INDEX_LIST, cfg) { 224 cfg.index_buffer_hi = 0; 225 cfg.primitive = AGX_PRIMITIVE_POINTS; 226 cfg.restart_enable = false; 227 cfg.index_size = 0; 228 cfg.index_buffer_size_present = false; 229 cfg.index_buffer_present = false; 230 cfg.index_count_present = true; 231 cfg.instance_count_present = true; 232 cfg.start_present = true; 233 cfg.unk_1_present = false; 234 cfg.indirect_buffer_present = false; 235 cfg.unk_2_present = false; 236 cfg.block_type = AGX_VDM_BLOCK_TYPE_INDEX_LIST; 237 } 238 239 agx_pack(&desc[1], INDEX_LIST_COUNT, cfg) { 240 cfg.count = count; 241 } 242 243 agx_pack(&desc[2], INDEX_LIST_INSTANCES, cfg) { 244 cfg.count = 1; 245 } 246 247 agx_pack(&desc[3], INDEX_LIST_START, cfg) { 248 cfg.start = patch * LIBAGX_TES_PATCH_ID_STRIDE; 249 } 250 } else { 251 /* For points mode with a single draw, we need to generate a trivial index 252 * buffer to stuff in the patch ID in the right place. 253 */ 254 global uint32_t *indices = libagx_draw(p, ctx->mode, false, patch, count); 255 256 if (ctx->mode == LIBAGX_TESS_MODE_COUNT) 257 return; 258 259 for (int i = 0; i < count; ++i) { 260 indices[i] = ctx->index_bias + i; 261 } 262 } 263} 264 265static void 266libagx_draw_empty(constant struct libagx_tess_args *p, 267 enum libagx_tess_mode mode, 268 enum libagx_tess_output_primitive output_primitive, 269 uint patch) 270{ 271 if (mode == LIBAGX_TESS_MODE_COUNT) { 272 p->counts[patch] = 0; 273 } else if (mode == LIBAGX_TESS_MODE_VDM) { 274 uint32_t words = (output_primitive == LIBAGX_TESS_OUTPUT_POINT) ? 4 : 6; 275 global uint32_t *desc = p->out_draws + (patch * words); 276 uint32_t nop_token = AGX_VDM_BLOCK_TYPE_BARRIER << 29; 277 278 for (uint32_t i = 0; i < words; ++i) { 279 desc[i] = nop_token; 280 } 281 } 282} 283 284/* 285 * Allocate heap memory for domain points for a patch. The allocation 286 * is recorded in the coord_allocs[] array, which is in elements. 287 */ 288static global struct libagx_tess_point * 289libagx_heap_alloc_points(constant struct libagx_tess_args *p, uint patch, 290 uint count) 291{ 292 /* If we're recording statistics, increment now. The statistic is for 293 * tessellation evaluation shader invocations, which is equal to the number 294 * of domain points generated. 295 */ 296 if (p->statistic) { 297 atomic_fetch_add((volatile atomic_uint *)(p->statistic), count); 298 } 299 300 uint32_t elsize_B = sizeof(struct libagx_tess_point); 301 uint32_t alloc_B = libagx_heap_alloc(p->heap, elsize_B * count); 302 uint32_t alloc_el = alloc_B / elsize_B; 303 304 p->coord_allocs[patch] = alloc_el; 305 return (global struct libagx_tess_point *)(((uintptr_t)p->heap->heap) + 306 alloc_B); 307} 308 309// Microsoft D3D11 Fixed Function Tessellator Reference - May 7, 2012 310// [email protected] 311 312#define LIBAGX_TESS_MIN_ODD_TESSELLATION_FACTOR 1 313#define LIBAGX_TESS_MAX_ODD_TESSELLATION_FACTOR 63 314#define LIBAGX_TESS_MIN_EVEN_TESSELLATION_FACTOR 2 315#define LIBAGX_TESS_MAX_EVEN_TESSELLATION_FACTOR 64 316 317// 2^(-16), min positive fixed point fraction 318#define EPSILON 0.0000152587890625f 319#define MIN_ODD_TESSFACTOR_PLUS_HALF_EPSILON \ 320 (LIBAGX_TESS_MIN_ODD_TESSELLATION_FACTOR + EPSILON / 2) 321 322static float clamp_factor(float factor, 323 enum libagx_tess_partitioning partitioning, 324 float maxf) 325{ 326 float lower = (partitioning == LIBAGX_TESS_PARTITIONING_FRACTIONAL_EVEN) 327 ? LIBAGX_TESS_MIN_EVEN_TESSELLATION_FACTOR 328 : LIBAGX_TESS_MIN_ODD_TESSELLATION_FACTOR; 329 330 float upper = (partitioning == LIBAGX_TESS_PARTITIONING_FRACTIONAL_ODD) 331 ? LIBAGX_TESS_MAX_ODD_TESSELLATION_FACTOR 332 : LIBAGX_TESS_MAX_EVEN_TESSELLATION_FACTOR; 333 334 // If any TessFactor will end up > 1 after floatToFixed conversion later, 335 // then force the inside TessFactors to be > 1 so there is a picture frame. 336 if (partitioning == LIBAGX_TESS_PARTITIONING_FRACTIONAL_ODD && 337 maxf > MIN_ODD_TESSFACTOR_PLUS_HALF_EPSILON) { 338 339 lower = LIBAGX_TESS_MIN_ODD_TESSELLATION_FACTOR + EPSILON; 340 } 341 342 factor = clamp(factor, lower, upper); 343 344 if (partitioning == LIBAGX_TESS_PARTITIONING_INTEGER) { 345 factor = ceil(factor); 346 } 347 348 return factor; 349} 350 351 352static FXP 353floatToFixed(const float input) 354{ 355 return mad(input, FXP_ONE, 0.5f); 356} 357 358static float 359fixedToFloat(const FXP input) 360{ 361 // Don't need to worry about special cases because the bounds are reasonable. 362 return ((float)input) / FXP_ONE; 363} 364 365static bool 366isOdd(const float input) 367{ 368 return ((int)input) & 1; 369} 370 371static FXP 372fxpCeil(const FXP input) 373{ 374 if (input & FXP_FRACTION_MASK) { 375 return (input & FXP_INTEGER_MASK) + FXP_ONE; 376 } 377 return input; 378} 379 380static FXP 381fxpFloor(const FXP input) 382{ 383 return (input & FXP_INTEGER_MASK); 384} 385 386static int 387PatchIndexValue(private struct CHWTessellator *ctx, int index) 388{ 389 if (ctx->bUsingPatchedIndices) { 390 // assumed remapped outide indices are > remapped inside vertices 391 if (index >= ctx->IndexPatchCtx.outsidePointIndexPatchBase) { 392 if (index == ctx->IndexPatchCtx.outsidePointIndexBadValue) 393 return ctx->IndexPatchCtx.outsidePointIndexReplacementValue; 394 else 395 return index + ctx->IndexPatchCtx.outsidePointIndexDeltaToRealValue; 396 } else { 397 if (index == ctx->IndexPatchCtx.insidePointIndexBadValue) 398 return ctx->IndexPatchCtx.insidePointIndexReplacementValue; 399 else 400 return index + ctx->IndexPatchCtx.insidePointIndexDeltaToRealValue; 401 } 402 } else if (ctx->bUsingPatchedIndices2) { 403 if (index >= ctx->IndexPatchCtx2.baseIndexToInvert) { 404 if (index == ctx->IndexPatchCtx2.cornerCaseBadValue) 405 return ctx->IndexPatchCtx2.cornerCaseReplacementValue; 406 else 407 return ctx->IndexPatchCtx2.indexInversionEndPoint - index; 408 } else if (index == ctx->IndexPatchCtx2.cornerCaseBadValue) { 409 return ctx->IndexPatchCtx2.cornerCaseReplacementValue; 410 } 411 } 412 413 return index; 414} 415 416static void 417DefinePoint(global struct libagx_tess_point *out, FXP fxpU, FXP fxpV) 418{ 419 out->u = fixedToFloat(fxpU); 420 out->v = fixedToFloat(fxpV); 421} 422 423static void 424DefineIndex(private struct CHWTessellator *ctx, int index, 425 int indexStorageOffset) 426{ 427 int patched = PatchIndexValue(ctx, index); 428 429 if (ctx->mode == LIBAGX_TESS_MODE_WITH_COUNTS) { 430 global uint32_t *indices = (global uint32_t *)ctx->Index; 431 indices[indexStorageOffset] = ctx->index_bias + patched; 432 } else { 433 global uint16_t *indices = (global uint16_t *)ctx->Index; 434 indices[indexStorageOffset] = patched; 435 } 436} 437 438static void 439DefineClockwiseTriangle(private struct CHWTessellator *ctx, int index0, 440 int index1, int index2, int indexStorageBaseOffset) 441{ 442 // inputs a clockwise triangle, stores a CW or CCW triangle per state state 443 bool cw = ctx->outputPrimitive == LIBAGX_TESS_OUTPUT_TRIANGLE_CW; 444 445 DefineIndex(ctx, index0, indexStorageBaseOffset); 446 DefineIndex(ctx, cw ? index1 : index2, indexStorageBaseOffset + 1); 447 DefineIndex(ctx, cw ? index2 : index1, indexStorageBaseOffset + 2); 448} 449 450static uint32_t 451RemoveMSB(uint32_t val) 452{ 453 uint32_t bit = val ? (1 << (31 - clz(val))) : 0; 454 return val & ~bit; 455} 456 457static int 458NumPointsForTessFactor(bool odd, FXP fxpTessFactor) 459{ 460 // Add epsilon for rounding and add 1 for odd 461 FXP f = fxpTessFactor + (odd ? (FXP_ONE + 1) : 1); 462 int r = fxpCeil(f / 2) >> (FXP_FRACTION_BITS - 1); 463 return odd ? r : r + 1; 464} 465 466static void 467ComputeTessFactorCtx(bool odd, FXP fxpTessFactor, 468 private TESS_FACTOR_CONTEXT *TessFactorCtx) 469{ 470 // fxpHalfTessFactor == 1/2 if TessFactor is 1, 471 // but we're pretending we are even. 472 FXP fxpHalfTessFactor = (fxpTessFactor + 1 /*round*/) / 2; 473 if (odd || (fxpHalfTessFactor == FXP_ONE_HALF)) { 474 fxpHalfTessFactor += FXP_ONE_HALF; 475 } 476 FXP fxpFloorHalfTessFactor = fxpFloor(fxpHalfTessFactor); 477 FXP fxpCeilHalfTessFactor = fxpCeil(fxpHalfTessFactor); 478 TessFactorCtx->fxpHalfTessFactorFraction = fxpHalfTessFactor - fxpFloorHalfTessFactor; 479 TessFactorCtx->numHalfTessFactorPoints = 480 (fxpCeilHalfTessFactor >> FXP_FRACTION_BITS); // for EVEN, we don't include the point always 481 // fixed at the midpoint of the TessFactor 482 if (fxpCeilHalfTessFactor == fxpFloorHalfTessFactor) { 483 TessFactorCtx->splitPointOnFloorHalfTessFactor = 484 /*pick value to cause this to be ignored*/ TessFactorCtx->numHalfTessFactorPoints + 1; 485 } else if (odd) { 486 if (fxpFloorHalfTessFactor == FXP_ONE) { 487 TessFactorCtx->splitPointOnFloorHalfTessFactor = 0; 488 } else { 489 TessFactorCtx->splitPointOnFloorHalfTessFactor = 490 (RemoveMSB((fxpFloorHalfTessFactor >> FXP_FRACTION_BITS) - 1) << 1) + 1; 491 } 492 } else { 493 TessFactorCtx->splitPointOnFloorHalfTessFactor = 494 (RemoveMSB(fxpFloorHalfTessFactor >> FXP_FRACTION_BITS) << 1) + 1; 495 } 496 int numFloorSegments = (fxpFloorHalfTessFactor * 2) >> FXP_FRACTION_BITS; 497 int numCeilSegments = (fxpCeilHalfTessFactor * 2) >> FXP_FRACTION_BITS; 498 if (odd) { 499 numFloorSegments -= 1; 500 numCeilSegments -= 1; 501 } 502 TessFactorCtx->fxpInvNumSegmentsOnFloorTessFactor = 503 floatToFixed(1.0f / (float)numFloorSegments); 504 TessFactorCtx->fxpInvNumSegmentsOnCeilTessFactor = 505 floatToFixed(1.0f / (float)numCeilSegments); 506} 507 508static FXP 509PlacePointIn1D(private const TESS_FACTOR_CONTEXT *TessFactorCtx, bool odd, 510 int point) 511{ 512 bool bFlip = point >= TessFactorCtx->numHalfTessFactorPoints; 513 514 if (bFlip) { 515 point = (TessFactorCtx->numHalfTessFactorPoints << 1) - point - odd; 516 } 517 518 // special casing middle since 16 bit fixed math below can't reproduce 0.5 exactly 519 if (point == TessFactorCtx->numHalfTessFactorPoints) 520 return FXP_ONE_HALF; 521 522 unsigned int indexOnCeilHalfTessFactor = point; 523 unsigned int indexOnFloorHalfTessFactor = indexOnCeilHalfTessFactor; 524 if (point > TessFactorCtx->splitPointOnFloorHalfTessFactor) { 525 indexOnFloorHalfTessFactor -= 1; 526 } 527 // For the fixed point multiplies below, we know the results are <= 16 bits 528 // because the locations on the halfTessFactor are <= half the number of 529 // segments for the total TessFactor. So a number divided by a number that 530 // is at least twice as big will give a result no bigger than 0.5 (which in 531 // fixed point is 16 bits in our case) 532 FXP fxpLocationOnFloorHalfTessFactor = 533 indexOnFloorHalfTessFactor * TessFactorCtx->fxpInvNumSegmentsOnFloorTessFactor; 534 FXP fxpLocationOnCeilHalfTessFactor = 535 indexOnCeilHalfTessFactor * TessFactorCtx->fxpInvNumSegmentsOnCeilTessFactor; 536 537 // Since we know the numbers calculated above are <= fixed point 0.5, and the 538 // equation below is just lerping between two values <= fixed point 0.5 539 // (0x00008000), then we know that the final result before shifting by 16 bits 540 // is no larger than 0x80000000. Once we shift that down by 16, we get the 541 // result of lerping 2 numbers <= 0.5, which is obviously at most 0.5 542 // (0x00008000) 543 FXP fxpLocation = 544 fxpLocationOnFloorHalfTessFactor * (FXP_ONE - TessFactorCtx->fxpHalfTessFactorFraction) + 545 fxpLocationOnCeilHalfTessFactor * (TessFactorCtx->fxpHalfTessFactorFraction); 546 fxpLocation = (fxpLocation + FXP_ONE_HALF /*round*/) >> FXP_FRACTION_BITS; // get back to n.16 547 if (bFlip) { 548 fxpLocation = FXP_ONE - fxpLocation; 549 } 550 return fxpLocation; 551} 552 553static void 554StitchRegular(private struct CHWTessellator *ctx, bool bTrapezoid, 555 DIAGONALS diagonals, int baseIndexOffset, int numInsideEdgePoints, 556 int insideEdgePointBaseOffset, int outsideEdgePointBaseOffset) 557{ 558 int insidePoint = insideEdgePointBaseOffset; 559 int outsidePoint = outsideEdgePointBaseOffset; 560 if (bTrapezoid) { 561 DefineClockwiseTriangle(ctx, outsidePoint, outsidePoint + 1, insidePoint, baseIndexOffset); 562 baseIndexOffset += 3; 563 outsidePoint++; 564 } 565 int p; 566 switch (diagonals) { 567 case DIAGONALS_INSIDE_TO_OUTSIDE: 568 // Diagonals pointing from inside edge forward towards outside edge 569 for (p = 0; p < numInsideEdgePoints - 1; p++) { 570 DefineClockwiseTriangle(ctx, insidePoint, outsidePoint, outsidePoint + 1, baseIndexOffset); 571 baseIndexOffset += 3; 572 573 DefineClockwiseTriangle(ctx, insidePoint, outsidePoint + 1, insidePoint + 1, 574 baseIndexOffset); 575 baseIndexOffset += 3; 576 insidePoint++; 577 outsidePoint++; 578 } 579 break; 580 case DIAGONALS_INSIDE_TO_OUTSIDE_EXCEPT_MIDDLE: // Assumes ODD tessellation 581 // Diagonals pointing from outside edge forward towards inside edge 582 583 // First half 584 for (p = 0; p < numInsideEdgePoints / 2 - 1; p++) { 585 DefineClockwiseTriangle(ctx, outsidePoint, outsidePoint + 1, insidePoint, baseIndexOffset); 586 baseIndexOffset += 3; 587 DefineClockwiseTriangle(ctx, insidePoint, outsidePoint + 1, insidePoint + 1, 588 baseIndexOffset); 589 baseIndexOffset += 3; 590 insidePoint++; 591 outsidePoint++; 592 } 593 594 // Middle 595 DefineClockwiseTriangle(ctx, outsidePoint, insidePoint + 1, insidePoint, baseIndexOffset); 596 baseIndexOffset += 3; 597 DefineClockwiseTriangle(ctx, outsidePoint, outsidePoint + 1, insidePoint + 1, 598 baseIndexOffset); 599 baseIndexOffset += 3; 600 insidePoint++; 601 outsidePoint++; 602 p += 2; 603 604 // Second half 605 for (; p < numInsideEdgePoints; p++) { 606 DefineClockwiseTriangle(ctx, outsidePoint, outsidePoint + 1, insidePoint, baseIndexOffset); 607 baseIndexOffset += 3; 608 DefineClockwiseTriangle(ctx, insidePoint, outsidePoint + 1, insidePoint + 1, 609 baseIndexOffset); 610 baseIndexOffset += 3; 611 insidePoint++; 612 outsidePoint++; 613 } 614 break; 615 case DIAGONALS_MIRRORED: 616 // First half, diagonals pointing from outside of outside edge to inside of 617 // inside edge 618 for (p = 0; p < numInsideEdgePoints / 2; p++) { 619 DefineClockwiseTriangle(ctx, outsidePoint, insidePoint + 1, insidePoint, baseIndexOffset); 620 baseIndexOffset += 3; 621 DefineClockwiseTriangle(ctx, outsidePoint, outsidePoint + 1, insidePoint + 1, 622 baseIndexOffset); 623 baseIndexOffset += 3; 624 insidePoint++; 625 outsidePoint++; 626 } 627 // Second half, diagonals pointing from inside of inside edge to outside of 628 // outside edge 629 for (; p < numInsideEdgePoints - 1; p++) { 630 DefineClockwiseTriangle(ctx, insidePoint, outsidePoint, outsidePoint + 1, baseIndexOffset); 631 baseIndexOffset += 3; 632 DefineClockwiseTriangle(ctx, insidePoint, outsidePoint + 1, insidePoint + 1, 633 baseIndexOffset); 634 baseIndexOffset += 3; 635 insidePoint++; 636 outsidePoint++; 637 } 638 break; 639 } 640 if (bTrapezoid) { 641 DefineClockwiseTriangle(ctx, outsidePoint, outsidePoint + 1, insidePoint, baseIndexOffset); 642 baseIndexOffset += 3; 643 } 644} 645 646// loop_start and loop_end give optimal loop bounds for 647// the stitching algorithm further below, for any given halfTssFactor. There 648// is probably a better way to encode this... 649// 650// Return the FIRST entry in finalPointPositionTable awhich is less than 651// halfTessFactor, except entry 0 and 1 which are set up to skip the loop. 652static int 653loop_start(int N) 654{ 655 if (N < 2) 656 return 1; 657 else if (N == 2) 658 return 17; 659 else if (N < 5) 660 return 9; 661 else if (N < 9) 662 return 5; 663 else if (N < 17) 664 return 3; 665 else 666 return 2; 667} 668 669// Return the LAST entry in finalPointPositionTable[] which is less than 670// halfTessFactor, except entry 0 and 1 which are set up to skip the loop. 671static int 672loop_end(int N) 673{ 674 if (N < 2) 675 return 0; 676 else if (N < 4) 677 return 17; 678 else if (N < 8) 679 return 25; 680 else if (N < 16) 681 return 29; 682 else if (N < 32) 683 return 31; 684 else 685 return 32; 686} 687 688// Tables to assist in the stitching of 2 rows of points having arbitrary 689// TessFactors. The stitching order is governed by Ruler Function vertex 690// split ordering (see external documentation). 691// 692// The contents of the finalPointPositionTable are where vertex i [0..33] 693// ends up on the half-edge at the max tessellation amount given 694// ruler-function split order. Recall the other half of an edge is mirrored, 695// so we only need to deal with one half. This table is used to decide when 696// to advance a point on the interior or exterior. It supports odd TessFactor 697// up to 65 and even TessFactor up to 64. 698 699/* TODO: Is this actually faster than a LUT? */ 700static uint32_t 701finalPointPositionTable(uint32_t x) 702{ 703 if (x == 0) 704 return 0; 705 if (x == 1) 706 return 0x20; 707 708 uint32_t shift; 709 if ((x & 1) == 0) { 710 shift = 1; 711 } else if ((x & 3) == 3) { 712 shift = 2; 713 } else if ((x & 7) == 5) { 714 shift = 3; 715 } else if (x != 17) { 716 shift = 4; 717 } else { 718 shift = 5; 719 } 720 721 // SWAR vectorized right-shift of (0x20, x) 722 // We're calculating `min(0xf, 0x20 >> shift) + (x >> shift)`. 723 uint32_t items_to_shift = x | (0x20 << 16); 724 uint32_t shifted = items_to_shift >> shift; 725 726 uint32_t bias = min(0xfu, shifted >> 16); 727 return bias + (shifted & 0xffff); 728} 729 730static void 731StitchTransition(private struct CHWTessellator *ctx, int baseIndexOffset, 732 int insideEdgePointBaseOffset, 733 int insideNumHalfTessFactorPoints, 734 bool insideEdgeTessFactorOdd, int outsideEdgePointBaseOffset, 735 int outsideNumHalfTessFactorPoints, bool outsideTessFactorOdd) 736{ 737 if (insideEdgeTessFactorOdd) { 738 insideNumHalfTessFactorPoints -= 1; 739 } 740 if (outsideTessFactorOdd) { 741 outsideNumHalfTessFactorPoints -= 1; 742 } 743 // Walk first half 744 int outsidePoint = outsideEdgePointBaseOffset; 745 int insidePoint = insideEdgePointBaseOffset; 746 747 // iStart,iEnd are a small optimization so the loop below doesn't have to go 748 // from 0 up to 31 749 int iStart = min(loop_start(insideNumHalfTessFactorPoints), 750 loop_start(outsideNumHalfTessFactorPoints)); 751 int iEnd = loop_end( 752 max(insideNumHalfTessFactorPoints, outsideNumHalfTessFactorPoints)); 753 754 // since we don't start the loop at 0 below, we need a special case. 755 if (0 < outsideNumHalfTessFactorPoints) { 756 // Advance outside 757 DefineClockwiseTriangle(ctx, outsidePoint, outsidePoint + 1, insidePoint, 758 baseIndexOffset); 759 baseIndexOffset += 3; 760 outsidePoint++; 761 } 762 763 for (int i = iStart; i <= iEnd; i++) { 764 int bound = finalPointPositionTable(i); 765 766 if (bound < insideNumHalfTessFactorPoints) { 767 // Advance inside 768 DefineClockwiseTriangle(ctx, insidePoint, outsidePoint, 769 insidePoint + 1, baseIndexOffset); 770 baseIndexOffset += 3; 771 insidePoint++; 772 } 773 if (bound < outsideNumHalfTessFactorPoints) { 774 // Advance outside 775 DefineClockwiseTriangle(ctx, outsidePoint, outsidePoint + 1, 776 insidePoint, baseIndexOffset); 777 baseIndexOffset += 3; 778 outsidePoint++; 779 } 780 } 781 782 if ((insideEdgeTessFactorOdd != outsideTessFactorOdd) || 783 insideEdgeTessFactorOdd) { 784 if (insideEdgeTessFactorOdd == outsideTessFactorOdd) { 785 // Quad in the middle 786 DefineClockwiseTriangle(ctx, insidePoint, outsidePoint, 787 insidePoint + 1, baseIndexOffset); 788 baseIndexOffset += 3; 789 DefineClockwiseTriangle(ctx, insidePoint + 1, outsidePoint, 790 outsidePoint + 1, baseIndexOffset); 791 baseIndexOffset += 3; 792 insidePoint++; 793 outsidePoint++; 794 } else if (!insideEdgeTessFactorOdd) { 795 // Triangle pointing inside 796 DefineClockwiseTriangle(ctx, insidePoint, outsidePoint, 797 outsidePoint + 1, baseIndexOffset); 798 baseIndexOffset += 3; 799 outsidePoint++; 800 } else { 801 // Triangle pointing outside 802 DefineClockwiseTriangle(ctx, insidePoint, outsidePoint, 803 insidePoint + 1, baseIndexOffset); 804 baseIndexOffset += 3; 805 insidePoint++; 806 } 807 } 808 809 // Walk second half. 810 for (int i = iEnd; i >= iStart; i--) { 811 int bound = finalPointPositionTable(i); 812 813 if (bound < outsideNumHalfTessFactorPoints) { 814 // Advance outside 815 DefineClockwiseTriangle(ctx, outsidePoint, outsidePoint + 1, 816 insidePoint, baseIndexOffset); 817 baseIndexOffset += 3; 818 outsidePoint++; 819 } 820 if (bound < insideNumHalfTessFactorPoints) { 821 // Advance inside 822 DefineClockwiseTriangle(ctx, insidePoint, outsidePoint, 823 insidePoint + 1, baseIndexOffset); 824 baseIndexOffset += 3; 825 insidePoint++; 826 } 827 } 828 // Below case is not needed if we didn't optimize loop above and made it run 829 // from 31 down to 0. 830 if (0 < outsideNumHalfTessFactorPoints) { 831 DefineClockwiseTriangle(ctx, outsidePoint, outsidePoint + 1, insidePoint, 832 baseIndexOffset); 833 baseIndexOffset += 3; 834 outsidePoint++; 835 } 836} 837 838void 839libagx_tess_isoline(constant struct libagx_tess_args *p, 840 enum libagx_tess_mode mode, 841 enum libagx_tess_partitioning partitioning, 842 enum libagx_tess_output_primitive output_primitive, 843 uint patch) 844{ 845 bool lineDensityOdd; 846 bool lineDetailOdd; 847 TESS_FACTOR_CONTEXT lineDensityTessFactorCtx; 848 TESS_FACTOR_CONTEXT lineDetailTessFactorCtx; 849 850 global float *factors = tess_factors(p, patch); 851 float TessFactor_V_LineDensity = factors[0]; 852 float TessFactor_U_LineDetail = factors[1]; 853 854 // Is the patch culled? NaN will pass. 855 if (!(TessFactor_V_LineDensity > 0) || !(TessFactor_U_LineDetail > 0)) { 856 libagx_draw_empty(p, mode, output_primitive, patch); 857 return; 858 } 859 860 // Clamp edge TessFactors 861 TessFactor_V_LineDensity = 862 clamp(TessFactor_V_LineDensity, 863 LIBAGX_TESS_MIN_ISOLINE_DENSITY_TESSELLATION_FACTOR, 864 LIBAGX_TESS_MAX_ISOLINE_DENSITY_TESSELLATION_FACTOR); 865 TessFactor_U_LineDetail = 866 clamp_factor(TessFactor_U_LineDetail, partitioning, 0); 867 868 // Process tessFactors 869 if (partitioning == LIBAGX_TESS_PARTITIONING_INTEGER) { 870 lineDetailOdd = isOdd(TessFactor_U_LineDetail); 871 } else { 872 lineDetailOdd = (partitioning == LIBAGX_TESS_PARTITIONING_FRACTIONAL_ODD); 873 } 874 875 FXP fxpTessFactor_U_LineDetail = floatToFixed(TessFactor_U_LineDetail); 876 877 ComputeTessFactorCtx(lineDetailOdd, fxpTessFactor_U_LineDetail, 878 &lineDetailTessFactorCtx); 879 int numPointsPerLine = 880 NumPointsForTessFactor(lineDetailOdd, fxpTessFactor_U_LineDetail); 881 882 TessFactor_V_LineDensity = ceil(TessFactor_V_LineDensity); 883 lineDensityOdd = isOdd(TessFactor_V_LineDensity); 884 FXP fxpTessFactor_V_LineDensity = floatToFixed(TessFactor_V_LineDensity); 885 ComputeTessFactorCtx(lineDensityOdd, fxpTessFactor_V_LineDensity, 886 &lineDensityTessFactorCtx); 887 888 // don't draw last line at V == 1. 889 int numLines = 890 NumPointsForTessFactor(lineDensityOdd, fxpTessFactor_V_LineDensity) - 1; 891 892 /* Points */ 893 uint num_points = numPointsPerLine * numLines; 894 if (mode != LIBAGX_TESS_MODE_COUNT) { 895 global struct libagx_tess_point *points = 896 libagx_heap_alloc_points(p, patch, num_points); 897 898 for (int line = 0, pointOffset = 0; line < numLines; line++) { 899 FXP fxpV = 900 PlacePointIn1D(&lineDensityTessFactorCtx, lineDensityOdd, line); 901 902 for (int point = 0; point < numPointsPerLine; point++) { 903 FXP fxpU = 904 PlacePointIn1D(&lineDetailTessFactorCtx, lineDetailOdd, point); 905 906 DefinePoint(&points[pointOffset++], fxpU, fxpV); 907 } 908 } 909 } 910 911 struct CHWTessellator ctx; 912 ctx.mode = mode; 913 ctx.index_bias = patch * LIBAGX_TES_PATCH_ID_STRIDE; 914 915 /* Connectivity */ 916 if (output_primitive != LIBAGX_TESS_OUTPUT_POINT) { 917 uint num_indices = numLines * (numPointsPerLine - 1) * 2; 918 ctx.Index = libagx_draw(p, mode, true, patch, num_indices); 919 920 if (mode == LIBAGX_TESS_MODE_COUNT) 921 return; 922 923 for (int line = 0, pointOffset = 0, indexOffset = 0; line < numLines; 924 line++) { 925 pointOffset++; 926 927 for (int point = 1; point < numPointsPerLine; point++) { 928 DefineIndex(&ctx, pointOffset - 1, indexOffset++); 929 DefineIndex(&ctx, pointOffset, indexOffset++); 930 pointOffset++; 931 } 932 } 933 } else { 934 libagx_draw_points(&ctx, p, patch, num_points); 935 } 936} 937 938void 939libagx_tess_tri(constant struct libagx_tess_args *p, enum libagx_tess_mode mode, 940 941 enum libagx_tess_partitioning partitioning, 942 enum libagx_tess_output_primitive output_primitive, uint patch) 943{ 944 global float *factors = tess_factors(p, patch); 945 float tessFactor_Ueq0 = factors[0]; 946 float tessFactor_Veq0 = factors[1]; 947 float tessFactor_Weq0 = factors[2]; 948 float insideTessFactor_f = factors[4]; 949 950 struct CHWTessellator ctx; 951 ctx.outputPrimitive = output_primitive; 952 ctx.Point = NULL; 953 ctx.Index = NULL; 954 ctx.mode = mode; 955 ctx.index_bias = patch * LIBAGX_TES_PATCH_ID_STRIDE; 956 ctx.bUsingPatchedIndices = false; 957 ctx.bUsingPatchedIndices2 = false; 958 959 // Is the patch culled? NaN will pass. 960 if (!(tessFactor_Ueq0 > 0) || !(tessFactor_Veq0 > 0) || 961 !(tessFactor_Weq0 > 0)) { 962 963 libagx_draw_empty(p, mode, output_primitive, patch); 964 965 return; 966 } 967 968 FXP outsideTessFactor[TRI_EDGES]; 969 FXP insideTessFactor; 970 bool outsideTessFactorOdd[TRI_EDGES]; 971 bool insideTessFactorOdd; 972 TESS_FACTOR_CONTEXT outsideTessFactorCtx[TRI_EDGES]; 973 TESS_FACTOR_CONTEXT insideTessFactorCtx; 974 // Stuff below is just specific to the traversal order 975 // this code happens to use to generate points/lines 976 int numPointsForOutsideEdge[TRI_EDGES]; 977 int numPointsForInsideTessFactor; 978 int insideEdgePointBaseOffset; 979 980 // Clamp TessFactors 981 tessFactor_Ueq0 = clamp_factor(tessFactor_Ueq0, partitioning, 0); 982 tessFactor_Veq0 = clamp_factor(tessFactor_Veq0, partitioning, 0); 983 tessFactor_Weq0 = clamp_factor(tessFactor_Weq0, partitioning, 0); 984 985 float maxf = max(max(tessFactor_Ueq0, tessFactor_Veq0), tessFactor_Weq0); 986 insideTessFactor_f = clamp_factor(insideTessFactor_f, partitioning, maxf); 987 // Note the above clamps map NaN to the lower bound 988 989 // Process tessFactors 990 float outsideTessFactor_f[TRI_EDGES] = {tessFactor_Ueq0, tessFactor_Veq0, 991 tessFactor_Weq0}; 992 if (partitioning == LIBAGX_TESS_PARTITIONING_INTEGER) { 993 for (int edge = 0; edge < TRI_EDGES; edge++) { 994 outsideTessFactorOdd[edge] = isOdd(outsideTessFactor_f[edge]); 995 } 996 insideTessFactorOdd = 997 isOdd(insideTessFactor_f) && (1.0f != insideTessFactor_f); 998 } else { 999 bool odd = (partitioning == LIBAGX_TESS_PARTITIONING_FRACTIONAL_ODD); 1000 1001 for (int edge = 0; edge < TRI_EDGES; edge++) { 1002 outsideTessFactorOdd[edge] = odd; 1003 } 1004 insideTessFactorOdd = odd; 1005 } 1006 1007 // Save fixed point TessFactors 1008 for (int edge = 0; edge < TRI_EDGES; edge++) { 1009 outsideTessFactor[edge] = floatToFixed(outsideTessFactor_f[edge]); 1010 } 1011 insideTessFactor = floatToFixed(insideTessFactor_f); 1012 1013 if (partitioning != LIBAGX_TESS_PARTITIONING_FRACTIONAL_EVEN) { 1014 // Special case if all TessFactors are 1 1015 if ((FXP_ONE == insideTessFactor) && 1016 (FXP_ONE == outsideTessFactor[Ueq0]) && 1017 (FXP_ONE == outsideTessFactor[Veq0]) && 1018 (FXP_ONE == outsideTessFactor[Weq0])) { 1019 1020 /* Just do minimum tess factor */ 1021 if (mode == LIBAGX_TESS_MODE_COUNT) { 1022 p->counts[patch] = 3; 1023 return; 1024 } 1025 1026 global struct libagx_tess_point *points = 1027 libagx_heap_alloc_points(p, patch, 3); 1028 1029 DefinePoint(&points[0], 0, 1030 FXP_ONE); // V=1 (beginning of Ueq0 edge VW) 1031 DefinePoint(&points[1], 0, 0); // W=1 (beginning of Veq0 edge WU) 1032 DefinePoint(&points[2], FXP_ONE, 1033 0); // U=1 (beginning of Weq0 edge UV) 1034 1035 if (output_primitive != LIBAGX_TESS_OUTPUT_POINT) { 1036 ctx.Index = libagx_draw(p, mode, false, patch, 3); 1037 1038 DefineClockwiseTriangle(&ctx, 0, 1, 2, 1039 /*indexStorageBaseOffset*/ 0); 1040 } else { 1041 libagx_draw_points(&ctx, p, patch, 3); 1042 } 1043 1044 return; 1045 } 1046 } 1047 1048 // Compute per-TessFactor metadata 1049 for (int edge = 0; edge < TRI_EDGES; edge++) { 1050 ComputeTessFactorCtx(outsideTessFactorOdd[edge], outsideTessFactor[edge], 1051 &outsideTessFactorCtx[edge]); 1052 } 1053 ComputeTessFactorCtx(insideTessFactorOdd, insideTessFactor, 1054 &insideTessFactorCtx); 1055 1056 // Compute some initial data. 1057 int NumPoints = 0; 1058 1059 // outside edge offsets and storage 1060 for (int edge = 0; edge < TRI_EDGES; edge++) { 1061 numPointsForOutsideEdge[edge] = NumPointsForTessFactor( 1062 outsideTessFactorOdd[edge], outsideTessFactor[edge]); 1063 NumPoints += numPointsForOutsideEdge[edge]; 1064 } 1065 NumPoints -= 3; 1066 1067 // inside edge offsets 1068 numPointsForInsideTessFactor = 1069 NumPointsForTessFactor(insideTessFactorOdd, insideTessFactor); 1070 { 1071 int pointCountMin = insideTessFactorOdd ? 4 : 3; 1072 // max() allows degenerate transition regions when inside TessFactor == 1 1073 numPointsForInsideTessFactor = 1074 max(pointCountMin, numPointsForInsideTessFactor); 1075 } 1076 1077 insideEdgePointBaseOffset = NumPoints; 1078 1079 // inside storage, including interior edges above 1080 { 1081 int interiorRings = (numPointsForInsideTessFactor >> 1) - 1; 1082 int even = insideTessFactorOdd ? 0 : 1; 1083 NumPoints += TRI_EDGES * (interiorRings * (interiorRings + even)) + even; 1084 } 1085 1086 /* GENERATE POINTS */ 1087 if (mode != LIBAGX_TESS_MODE_COUNT) { 1088 ctx.Point = libagx_heap_alloc_points(p, patch, NumPoints); 1089 1090 // Generate exterior ring edge points, clockwise starting from point V 1091 // (VW, the U==0 edge) 1092 int pointOffset = 0; 1093 for (int edge = 0; edge < TRI_EDGES; edge++) { 1094 int odd = edge & 0x1; 1095 int endPoint = numPointsForOutsideEdge[edge] - 1; 1096 // don't include end, since next edge starts with it. 1097 for (int p = 0; p < endPoint; p++, pointOffset++) { 1098 // whether to reverse point order given we are defining V or U (W 1099 // implicit): edge0, VW, has V decreasing, so reverse 1D points 1100 // below edge1, WU, has U increasing, so don't reverse 1D points 1101 // below edge2, UV, has U decreasing, so reverse 1D points below 1102 int q = odd ? p : endPoint - p; 1103 1104 FXP fxpParam = PlacePointIn1D(&outsideTessFactorCtx[edge], 1105 outsideTessFactorOdd[edge], q); 1106 if (edge == 0) { 1107 DefinePoint(&ctx.Point[pointOffset], 0, fxpParam); 1108 } else { 1109 DefinePoint(&ctx.Point[pointOffset], fxpParam, 1110 (edge == 2) ? FXP_ONE - fxpParam : 0); 1111 } 1112 } 1113 } 1114 1115 // Generate interior ring points, clockwise spiralling in 1116 int numRings = (numPointsForInsideTessFactor >> 1); 1117 for (int ring = 1; ring < numRings; ring++) { 1118 int startPoint = ring; 1119 int endPoint = numPointsForInsideTessFactor - 1 - startPoint; 1120 1121 int perpendicularAxisPoint = startPoint; 1122 FXP fxpPerpParam = PlacePointIn1D( 1123 &insideTessFactorCtx, insideTessFactorOdd, perpendicularAxisPoint); 1124 1125 // Map location to the right size in 1126 // barycentric space. We know this fixed 1127 // point math won't over/underflow 1128 fxpPerpParam *= FXP_TWO_THIRDS; 1129 fxpPerpParam = (fxpPerpParam + FXP_ONE_HALF /*round*/) >> 1130 FXP_FRACTION_BITS; // get back to n.16 1131 1132 for (int edge = 0; edge < TRI_EDGES; edge++) { 1133 int odd = edge & 0x1; 1134 1135 // don't include end: next edge starts with it. 1136 for (int p = startPoint; p < endPoint; p++, pointOffset++) { 1137 // whether to reverse point given we are defining V or U (W 1138 // implicit): edge0, VW, has V decreasing, so reverse 1D points 1139 // below edge1, WU, has U increasing, so don't reverse 1D points 1140 // below edge2, UV, has U decreasing, so reverse 1D points below 1141 int q = odd ? p : endPoint - (p - startPoint); 1142 1143 FXP fxpParam = 1144 PlacePointIn1D(&insideTessFactorCtx, insideTessFactorOdd, q); 1145 // edge0 VW, has perpendicular parameter U constant 1146 // edge1 WU, has perpendicular parameter V constant 1147 // edge2 UV, has perpendicular parameter W constant 1148 // reciprocal is the rate of change of edge-parallel parameters 1149 // as they are pushed into the triangle 1150 const unsigned int deriv = 2; 1151 1152 // we know this fixed point math won't over/underflow 1153 FXP tmp = fxpParam - (fxpPerpParam + 1 /*round*/) / deriv; 1154 1155 DefinePoint(&ctx.Point[pointOffset], 1156 edge > 0 ? tmp : fxpPerpParam, 1157 edge == 0 ? tmp 1158 : edge == 1 ? fxpPerpParam 1159 : FXP_ONE - tmp - fxpPerpParam); 1160 } 1161 } 1162 } 1163 if (!insideTessFactorOdd) { 1164 // Last point is the point at the center. 1165 DefinePoint(&ctx.Point[pointOffset], FXP_ONE_THIRD, FXP_ONE_THIRD); 1166 } 1167 } 1168 1169 if (output_primitive == LIBAGX_TESS_OUTPUT_POINT) { 1170 libagx_draw_points(&ctx, p, patch, NumPoints); 1171 return; 1172 } 1173 1174 { 1175 // Generate primitives for all the concentric rings, one side at a time 1176 // for each ring +1 is so even tess includes the center point, which we 1177 // want to now 1178 int numRings = ((numPointsForInsideTessFactor + 1) >> 1); 1179 1180 int NumIndices = 0; 1181 { 1182 assert(numRings >= 2 && "invariant"); 1183 int OuterPoints = numPointsForOutsideEdge[0] + 1184 numPointsForOutsideEdge[1] + 1185 numPointsForOutsideEdge[2]; 1186 1187 int numRings18 = numRings * 18; 1188 NumIndices = ((numRings18 - 27) * numPointsForInsideTessFactor) + 1189 (3 * OuterPoints) - (numRings18 * (numRings - 1)) + 1190 (insideTessFactorOdd ? 3 : 0); 1191 } 1192 1193 // Generate the draw and allocate the index buffer now that we know the size 1194 ctx.Index = libagx_draw(p, mode, false, patch, NumIndices); 1195 1196 if (mode == LIBAGX_TESS_MODE_COUNT) 1197 return; 1198 1199 int insideOffset = insideEdgePointBaseOffset; 1200 int outsideEdgePointBaseOffset = 0; 1201 1202 NumIndices = 0; 1203 for (int ring = 1; ring < numRings; ring++) { 1204 int numPointsForInsideEdge = numPointsForInsideTessFactor - 2 * ring; 1205 int edge0InsidePointBaseOffset = insideOffset; 1206 int edge0OutsidePointBaseOffset = outsideEdgePointBaseOffset; 1207 for (int edge = 0; edge < TRI_EDGES; edge++) { 1208 int outsidePoints = ring == 1 ? numPointsForOutsideEdge[edge] 1209 : (numPointsForInsideEdge + 2); 1210 1211 int numTriangles = numPointsForInsideEdge + outsidePoints - 2; 1212 1213 int insideBaseOffset; 1214 int outsideBaseOffset; 1215 if (edge == 2) { 1216 ctx.IndexPatchCtx.insidePointIndexDeltaToRealValue = 1217 insideOffset; 1218 ctx.IndexPatchCtx.insidePointIndexBadValue = 1219 numPointsForInsideEdge - 1; 1220 ctx.IndexPatchCtx.insidePointIndexReplacementValue = 1221 edge0InsidePointBaseOffset; 1222 ctx.IndexPatchCtx.outsidePointIndexPatchBase = 1223 ctx.IndexPatchCtx.insidePointIndexBadValue + 1224 1; // past inside patched index range 1225 ctx.IndexPatchCtx.outsidePointIndexDeltaToRealValue = 1226 outsideEdgePointBaseOffset - 1227 ctx.IndexPatchCtx.outsidePointIndexPatchBase; 1228 ctx.IndexPatchCtx.outsidePointIndexBadValue = 1229 ctx.IndexPatchCtx.outsidePointIndexPatchBase + outsidePoints - 1230 1; 1231 ctx.IndexPatchCtx.outsidePointIndexReplacementValue = 1232 edge0OutsidePointBaseOffset; 1233 ctx.bUsingPatchedIndices = true; 1234 insideBaseOffset = 0; 1235 outsideBaseOffset = ctx.IndexPatchCtx.outsidePointIndexPatchBase; 1236 } else { 1237 insideBaseOffset = insideOffset; 1238 outsideBaseOffset = outsideEdgePointBaseOffset; 1239 } 1240 if (ring == 1) { 1241 StitchTransition( 1242 &ctx, /*baseIndexOffset: */ NumIndices, insideBaseOffset, 1243 insideTessFactorCtx.numHalfTessFactorPoints, 1244 insideTessFactorOdd, outsideBaseOffset, 1245 outsideTessFactorCtx[edge].numHalfTessFactorPoints, 1246 outsideTessFactorOdd[edge]); 1247 } else { 1248 StitchRegular(&ctx, /*bTrapezoid*/ true, DIAGONALS_MIRRORED, 1249 /*baseIndexOffset: */ NumIndices, 1250 numPointsForInsideEdge, insideBaseOffset, 1251 outsideBaseOffset); 1252 } 1253 if (2 == edge) { 1254 ctx.bUsingPatchedIndices = false; 1255 } 1256 NumIndices += numTriangles * 3; 1257 outsideEdgePointBaseOffset += outsidePoints - 1; 1258 insideOffset += numPointsForInsideEdge - 1; 1259 } 1260 } 1261 if (insideTessFactorOdd) { 1262 // Triangulate center (a single triangle) 1263 DefineClockwiseTriangle(&ctx, outsideEdgePointBaseOffset, 1264 outsideEdgePointBaseOffset + 1, 1265 outsideEdgePointBaseOffset + 2, NumIndices); 1266 NumIndices += 3; 1267 } 1268 } 1269} 1270 1271void 1272libagx_tess_quad(constant struct libagx_tess_args *p, 1273 enum libagx_tess_mode mode, 1274 enum libagx_tess_partitioning partitioning, 1275 enum libagx_tess_output_primitive output_primitive, uint patch) 1276{ 1277 global float *factors = tess_factors(p, patch); 1278 1279 float tessFactor_Ueq0 = factors[0]; 1280 float tessFactor_Veq0 = factors[1]; 1281 float tessFactor_Ueq1 = factors[2]; 1282 float tessFactor_Veq1 = factors[3]; 1283 1284 float insideTessFactor_U = factors[4]; 1285 float insideTessFactor_V = factors[5]; 1286 1287 // TODO: fix designated initializer optimization in NIR 1288 struct CHWTessellator ctx; 1289 ctx.outputPrimitive = output_primitive; 1290 ctx.Point = NULL; 1291 ctx.Index = NULL; 1292 ctx.mode = mode; 1293 ctx.index_bias = patch * LIBAGX_TES_PATCH_ID_STRIDE; 1294 ctx.bUsingPatchedIndices = false; 1295 ctx.bUsingPatchedIndices2 = false; 1296 1297 // Is the patch culled? 1298 if (!(tessFactor_Ueq0 > 0) || // NaN will pass 1299 !(tessFactor_Veq0 > 0) || !(tessFactor_Ueq1 > 0) || 1300 !(tessFactor_Veq1 > 0)) { 1301 libagx_draw_empty(p, mode, output_primitive, patch); 1302 return; 1303 } 1304 1305 FXP outsideTessFactor[QUAD_EDGES]; 1306 FXP insideTessFactor[QUAD_AXES]; 1307 bool outsideTessFactorOdd[QUAD_EDGES]; 1308 bool insideTessFactorOdd[QUAD_AXES]; 1309 TESS_FACTOR_CONTEXT outsideTessFactorCtx[QUAD_EDGES]; 1310 TESS_FACTOR_CONTEXT insideTessFactorCtx[QUAD_AXES]; 1311 // Stuff below is just specific to the traversal order 1312 // this code happens to use to generate points/lines 1313 int numPointsForOutsideEdge[QUAD_EDGES]; 1314 int numPointsForInsideTessFactor[QUAD_AXES]; 1315 int insideEdgePointBaseOffset; 1316 1317 // Clamp edge TessFactors 1318 tessFactor_Ueq0 = clamp_factor(tessFactor_Ueq0, partitioning, 0); 1319 tessFactor_Veq0 = clamp_factor(tessFactor_Veq0, partitioning, 0); 1320 tessFactor_Ueq1 = clamp_factor(tessFactor_Ueq1, partitioning, 0); 1321 tessFactor_Veq1 = clamp_factor(tessFactor_Veq1, partitioning, 0); 1322 1323 float maxf = max(max(max(tessFactor_Ueq0, tessFactor_Veq0), 1324 max(tessFactor_Ueq1, tessFactor_Veq1)), 1325 max(insideTessFactor_U, insideTessFactor_V)); 1326 1327 insideTessFactor_U = clamp_factor(insideTessFactor_U, partitioning, maxf); 1328 insideTessFactor_V = clamp_factor(insideTessFactor_V, partitioning, maxf); 1329 // Note the above clamps map NaN to lowerBound 1330 1331 // Process tessFactors 1332 float outsideTessFactor_f[QUAD_EDGES] = {tessFactor_Ueq0, tessFactor_Veq0, 1333 tessFactor_Ueq1, tessFactor_Veq1}; 1334 float insideTessFactor_f[QUAD_AXES] = {insideTessFactor_U, 1335 insideTessFactor_V}; 1336 int edge, axis; 1337 if (partitioning == LIBAGX_TESS_PARTITIONING_INTEGER) { 1338 for (edge = 0; edge < QUAD_EDGES; edge++) { 1339 outsideTessFactorOdd[edge] = isOdd(outsideTessFactor_f[edge]); 1340 } 1341 for (axis = 0; axis < QUAD_AXES; axis++) { 1342 insideTessFactorOdd[axis] = isOdd(insideTessFactor_f[axis]) && 1343 (1.0f != insideTessFactor_f[axis]); 1344 } 1345 } else { 1346 bool odd = (partitioning == LIBAGX_TESS_PARTITIONING_FRACTIONAL_ODD); 1347 1348 for (edge = 0; edge < QUAD_EDGES; edge++) { 1349 outsideTessFactorOdd[edge] = odd; 1350 } 1351 insideTessFactorOdd[U] = insideTessFactorOdd[V] = odd; 1352 } 1353 1354 // Save fixed point TessFactors 1355 for (edge = 0; edge < QUAD_EDGES; edge++) { 1356 outsideTessFactor[edge] = floatToFixed(outsideTessFactor_f[edge]); 1357 } 1358 for (axis = 0; axis < QUAD_AXES; axis++) { 1359 insideTessFactor[axis] = floatToFixed(insideTessFactor_f[axis]); 1360 } 1361 1362 if (partitioning != LIBAGX_TESS_PARTITIONING_FRACTIONAL_EVEN) { 1363 // Special case if all TessFactors are 1 1364 if ((FXP_ONE == insideTessFactor[U]) && 1365 (FXP_ONE == insideTessFactor[V]) && 1366 (FXP_ONE == outsideTessFactor[Ueq0]) && 1367 (FXP_ONE == outsideTessFactor[Veq0]) && 1368 (FXP_ONE == outsideTessFactor[Ueq1]) && 1369 (FXP_ONE == outsideTessFactor[Veq1])) { 1370 1371 /* Just do minimum tess factor */ 1372 if (output_primitive != LIBAGX_TESS_OUTPUT_POINT) { 1373 ctx.Index = libagx_draw(p, mode, false, patch, 6); 1374 if (mode == LIBAGX_TESS_MODE_COUNT) 1375 return; 1376 1377 DefineClockwiseTriangle(&ctx, 0, 1, 3, /*indexStorageOffset*/ 0); 1378 DefineClockwiseTriangle(&ctx, 1, 2, 3, /*indexStorageOffset*/ 3); 1379 } else { 1380 libagx_draw_points(&ctx, p, patch, 4); 1381 } 1382 1383 global struct libagx_tess_point *points = 1384 libagx_heap_alloc_points(p, patch, 4); 1385 1386 DefinePoint(&points[0], 0, 0); 1387 DefinePoint(&points[1], FXP_ONE, 0); 1388 DefinePoint(&points[2], FXP_ONE, FXP_ONE); 1389 DefinePoint(&points[3], 0, FXP_ONE); 1390 return; 1391 } 1392 } 1393 1394 // Compute TessFactor-specific metadata 1395 for (int edge = 0; edge < QUAD_EDGES; edge++) { 1396 ComputeTessFactorCtx(outsideTessFactorOdd[edge], outsideTessFactor[edge], 1397 &outsideTessFactorCtx[edge]); 1398 } 1399 1400 for (int axis = 0; axis < QUAD_AXES; axis++) { 1401 ComputeTessFactorCtx(insideTessFactorOdd[axis], insideTessFactor[axis], 1402 &insideTessFactorCtx[axis]); 1403 } 1404 1405 int NumPoints = 0; 1406 1407 // outside edge offsets and storage 1408 for (int edge = 0; edge < QUAD_EDGES; edge++) { 1409 numPointsForOutsideEdge[edge] = NumPointsForTessFactor( 1410 outsideTessFactorOdd[edge], outsideTessFactor[edge]); 1411 NumPoints += numPointsForOutsideEdge[edge]; 1412 } 1413 NumPoints -= 4; 1414 1415 // inside edge offsets 1416 for (int axis = 0; axis < QUAD_AXES; axis++) { 1417 numPointsForInsideTessFactor[axis] = NumPointsForTessFactor( 1418 insideTessFactorOdd[axis], insideTessFactor[axis]); 1419 int pointCountMin = insideTessFactorOdd[axis] ? 4 : 3; 1420 // max() allows degenerate transition regions when inside TessFactor == 1 1421 numPointsForInsideTessFactor[axis] = 1422 max(pointCountMin, numPointsForInsideTessFactor[axis]); 1423 } 1424 1425 insideEdgePointBaseOffset = NumPoints; 1426 1427 // inside storage, including interior edges above 1428 int numInteriorPoints = (numPointsForInsideTessFactor[U] - 2) * 1429 (numPointsForInsideTessFactor[V] - 2); 1430 NumPoints += numInteriorPoints; 1431 1432 if (mode != LIBAGX_TESS_MODE_COUNT) { 1433 ctx.Point = libagx_heap_alloc_points(p, patch, NumPoints); 1434 1435 // Generate exterior ring edge points, clockwise from top-left 1436 int pointOffset = 0; 1437 for (int edge = 0; edge < QUAD_EDGES; edge++) { 1438 int odd = edge & 0x1; 1439 // don't include end, since next edge starts with it. 1440 int endPoint = numPointsForOutsideEdge[edge] - 1; 1441 for (int p = 0; p < endPoint; p++, pointOffset++) { 1442 FXP fxpParam; 1443 int q = 1444 ((edge == 1) || (edge == 2)) ? p : endPoint - p; // reverse order 1445 fxpParam = PlacePointIn1D(&outsideTessFactorCtx[edge], 1446 outsideTessFactorOdd[edge], q); 1447 if (odd) { 1448 DefinePoint(&ctx.Point[pointOffset], fxpParam, 1449 (edge == 3) ? FXP_ONE : 0); 1450 } else { 1451 DefinePoint(&ctx.Point[pointOffset], (edge == 2) ? FXP_ONE : 0, 1452 fxpParam); 1453 } 1454 } 1455 } 1456 1457 // Generate interior ring points, clockwise from (U==0,V==1) (bottom-left) 1458 // spiralling toward center 1459 int minNumPointsForTessFactor = 1460 min(numPointsForInsideTessFactor[U], numPointsForInsideTessFactor[V]); 1461 // note for even tess we aren't counting center point here. 1462 int numRings = (minNumPointsForTessFactor >> 1); 1463 1464 for (int ring = 1; ring < numRings; ring++) { 1465 int startPoint = ring; 1466 int endPoint[QUAD_AXES] = { 1467 numPointsForInsideTessFactor[U] - 1 - startPoint, 1468 numPointsForInsideTessFactor[V] - 1 - startPoint, 1469 }; 1470 1471 for (int edge = 0; edge < QUAD_EDGES; edge++) { 1472 int odd[QUAD_AXES] = {edge & 0x1, ((edge + 1) & 0x1)}; 1473 int perpendicularAxisPoint = 1474 (edge < 2) ? startPoint : endPoint[odd[0]]; 1475 FXP fxpPerpParam = PlacePointIn1D(&insideTessFactorCtx[odd[0]], 1476 insideTessFactorOdd[odd[0]], 1477 perpendicularAxisPoint); 1478 1479 for (int p = startPoint; p < endPoint[odd[1]]; p++, 1480 pointOffset++) // don't include end: next edge starts with 1481 // it. 1482 { 1483 int q = ((edge == 1) || (edge == 2)) 1484 ? p 1485 : endPoint[odd[1]] - (p - startPoint); 1486 FXP fxpParam = PlacePointIn1D(&insideTessFactorCtx[odd[1]], 1487 insideTessFactorOdd[odd[1]], q); 1488 if (odd[1]) { 1489 DefinePoint(&ctx.Point[pointOffset], fxpPerpParam, fxpParam); 1490 } else { 1491 DefinePoint(&ctx.Point[pointOffset], fxpParam, fxpPerpParam); 1492 } 1493 } 1494 } 1495 } 1496 // For even tessellation, the inner "ring" is degenerate - a row of points 1497 if ((numPointsForInsideTessFactor[U] > numPointsForInsideTessFactor[V]) && 1498 !insideTessFactorOdd[V]) { 1499 int startPoint = numRings; 1500 int endPoint = numPointsForInsideTessFactor[U] - 1 - startPoint; 1501 for (int p = startPoint; p <= endPoint; p++, pointOffset++) { 1502 FXP fxpParam = PlacePointIn1D(&insideTessFactorCtx[U], 1503 insideTessFactorOdd[U], p); 1504 DefinePoint(&ctx.Point[pointOffset], fxpParam, FXP_ONE_HALF); 1505 } 1506 } else if ((numPointsForInsideTessFactor[V] >= 1507 numPointsForInsideTessFactor[U]) && 1508 !insideTessFactorOdd[U]) { 1509 int startPoint = numRings; 1510 int endPoint = numPointsForInsideTessFactor[V] - 1 - startPoint; 1511 for (int p = endPoint; p >= startPoint; p--, pointOffset++) { 1512 FXP fxpParam = PlacePointIn1D(&insideTessFactorCtx[V], 1513 insideTessFactorOdd[V], p); 1514 DefinePoint(&ctx.Point[pointOffset], FXP_ONE_HALF, fxpParam); 1515 } 1516 } 1517 } 1518 1519 if (output_primitive == LIBAGX_TESS_OUTPUT_POINT) { 1520 libagx_draw_points(&ctx, p, patch, NumPoints); 1521 return; 1522 } 1523 1524 /* CONNECTIVITY */ 1525 { 1526 // Generate primitives for all the concentric rings, one side at a time 1527 // for each ring. +1 is so even tess includes the center point 1528 int numPointRowsToCenter[QUAD_AXES] = { 1529 (numPointsForInsideTessFactor[U] + 1) >> 1, 1530 (numPointsForInsideTessFactor[V] + 1) >> 1, 1531 }; 1532 1533 int numRings = min(numPointRowsToCenter[U], numPointRowsToCenter[V]); 1534 1535 /* Calculate # of indices so we can allocate */ 1536 { 1537 /* numPointsForInsideTessFactor >= 3 so numRings >= 2 */ 1538 assert(numRings >= 2); 1539 1540 /* Handle main case */ 1541 int OuterPoints = 1542 numPointsForOutsideEdge[0] + numPointsForOutsideEdge[1] + 1543 numPointsForOutsideEdge[2] + numPointsForOutsideEdge[3]; 1544 1545 int InnerPoints = 1546 numPointsForInsideTessFactor[U] + numPointsForInsideTessFactor[V]; 1547 1548 int NumIndices = (OuterPoints * 3) + (12 * numRings * InnerPoints) - 1549 (InnerPoints * 18) - (24 * numRings * (numRings - 1)); 1550 1551 /* Determine major/minor axes */ 1552 bool U_major = 1553 (numPointsForInsideTessFactor[U] > numPointsForInsideTessFactor[V]); 1554 unsigned M = U_major ? U : V; 1555 unsigned m = U_major ? V : U; 1556 1557 /* Handle degenerate ring */ 1558 if (insideTessFactorOdd[m]) { 1559 assert(numPointsForInsideTessFactor[M] >= 1560 numPointsForInsideTessFactor[m]); 1561 1562 NumIndices += 12 * ((numPointsForInsideTessFactor[M] >> 1) - 1563 (numPointsForInsideTessFactor[m] >> 1)); 1564 NumIndices += (insideTessFactorOdd[M] ? 6 : 12); 1565 } 1566 1567 // Generate the draw and allocate the index buffer with the size 1568 ctx.Index = libagx_draw(p, mode, false, patch, NumIndices); 1569 } 1570 1571 if (mode == LIBAGX_TESS_MODE_COUNT) 1572 return; 1573 1574 int degeneratePointRing[QUAD_AXES] = { 1575 // Even partitioning causes degenerate row of points, 1576 // which results in exceptions to the point ordering conventions 1577 // when travelling around the rings counterclockwise. 1578 !insideTessFactorOdd[V] ? numPointRowsToCenter[V] - 1 : -1, 1579 !insideTessFactorOdd[U] ? numPointRowsToCenter[U] - 1 : -1, 1580 }; 1581 1582 int numPointsForOutsideEdge_[QUAD_EDGES] = { 1583 numPointsForOutsideEdge[Ueq0], 1584 numPointsForOutsideEdge[Veq0], 1585 numPointsForOutsideEdge[Ueq1], 1586 numPointsForOutsideEdge[Veq1], 1587 }; 1588 1589 int insideEdgePointBaseOffset_ = insideEdgePointBaseOffset; 1590 int outsideEdgePointBaseOffset = 0; 1591 1592 int NumIndices = 0; 1593 1594 for (int ring = 1; ring < numRings; ring++) { 1595 int numPointsForInsideEdge[QUAD_AXES] = { 1596 numPointsForInsideTessFactor[U] - 2 * ring, 1597 numPointsForInsideTessFactor[V] - 2 * ring}; 1598 1599 int edge0InsidePointBaseOffset = insideEdgePointBaseOffset_; 1600 int edge0OutsidePointBaseOffset = outsideEdgePointBaseOffset; 1601 1602 for (int edge = 0; edge < QUAD_EDGES; edge++) { 1603 int odd = (edge + 1) & 0x1; 1604 1605 int numTriangles = 1606 numPointsForInsideEdge[odd] + numPointsForOutsideEdge_[edge] - 2; 1607 int insideBaseOffset; 1608 int outsideBaseOffset; 1609 1610 // We need to patch the indexing so Stitch() can think it sees 2 1611 // sequentially increasing rows of points, even though we have 1612 // wrapped around to the end of the inner and outer ring's points, 1613 // so the last point is really the first point for the ring. We make 1614 // it so that when Stitch() calls AddIndex(), that function will do 1615 // any necessary index adjustment. 1616 if (edge == 3) { 1617 if (ring == degeneratePointRing[odd]) { 1618 ctx.IndexPatchCtx2.baseIndexToInvert = 1619 insideEdgePointBaseOffset_ + 1; 1620 ctx.IndexPatchCtx2.cornerCaseBadValue = 1621 outsideEdgePointBaseOffset + 1622 numPointsForOutsideEdge_[edge] - 1; 1623 ctx.IndexPatchCtx2.cornerCaseReplacementValue = 1624 edge0OutsidePointBaseOffset; 1625 ctx.IndexPatchCtx2.indexInversionEndPoint = 1626 (ctx.IndexPatchCtx2.baseIndexToInvert << 1) - 1; 1627 insideBaseOffset = ctx.IndexPatchCtx2.baseIndexToInvert; 1628 outsideBaseOffset = outsideEdgePointBaseOffset; 1629 ctx.bUsingPatchedIndices2 = true; 1630 } else { 1631 ctx.IndexPatchCtx.insidePointIndexDeltaToRealValue = 1632 insideEdgePointBaseOffset_; 1633 ctx.IndexPatchCtx.insidePointIndexBadValue = 1634 numPointsForInsideEdge[odd] - 1; 1635 ctx.IndexPatchCtx.insidePointIndexReplacementValue = 1636 edge0InsidePointBaseOffset; 1637 ctx.IndexPatchCtx.outsidePointIndexPatchBase = 1638 ctx.IndexPatchCtx.insidePointIndexBadValue + 1639 1; // past inside patched index range 1640 ctx.IndexPatchCtx.outsidePointIndexDeltaToRealValue = 1641 outsideEdgePointBaseOffset - 1642 ctx.IndexPatchCtx.outsidePointIndexPatchBase; 1643 ctx.IndexPatchCtx.outsidePointIndexBadValue = 1644 ctx.IndexPatchCtx.outsidePointIndexPatchBase + 1645 numPointsForOutsideEdge_[edge] - 1; 1646 ctx.IndexPatchCtx.outsidePointIndexReplacementValue = 1647 edge0OutsidePointBaseOffset; 1648 1649 insideBaseOffset = 0; 1650 outsideBaseOffset = 1651 ctx.IndexPatchCtx.outsidePointIndexPatchBase; 1652 ctx.bUsingPatchedIndices = true; 1653 } 1654 } else if ((edge == 2) && (ring == degeneratePointRing[odd])) { 1655 ctx.IndexPatchCtx2.baseIndexToInvert = 1656 insideEdgePointBaseOffset_; 1657 ctx.IndexPatchCtx2.cornerCaseBadValue = -1; // unused 1658 ctx.IndexPatchCtx2.cornerCaseReplacementValue = -1; // unused 1659 ctx.IndexPatchCtx2.indexInversionEndPoint = 1660 ctx.IndexPatchCtx2.baseIndexToInvert << 1; 1661 insideBaseOffset = ctx.IndexPatchCtx2.baseIndexToInvert; 1662 outsideBaseOffset = outsideEdgePointBaseOffset; 1663 ctx.bUsingPatchedIndices2 = true; 1664 } else { 1665 insideBaseOffset = insideEdgePointBaseOffset_; 1666 outsideBaseOffset = outsideEdgePointBaseOffset; 1667 } 1668 if (ring == 1) { 1669 StitchTransition( 1670 &ctx, /*baseIndexOffset: */ NumIndices, insideBaseOffset, 1671 insideTessFactorCtx[odd].numHalfTessFactorPoints, 1672 insideTessFactorOdd[odd], outsideBaseOffset, 1673 outsideTessFactorCtx[edge].numHalfTessFactorPoints, 1674 outsideTessFactorOdd[edge]); 1675 } else { 1676 StitchRegular(&ctx, /*bTrapezoid*/ true, DIAGONALS_MIRRORED, 1677 /*baseIndexOffset: */ NumIndices, 1678 numPointsForInsideEdge[odd], insideBaseOffset, 1679 outsideBaseOffset); 1680 } 1681 ctx.bUsingPatchedIndices = false; 1682 ctx.bUsingPatchedIndices2 = false; 1683 NumIndices += numTriangles * 3; 1684 outsideEdgePointBaseOffset += numPointsForOutsideEdge_[edge] - 1; 1685 if ((edge == 2) && (ring == degeneratePointRing[odd])) { 1686 insideEdgePointBaseOffset_ -= numPointsForInsideEdge[odd] - 1; 1687 } else { 1688 insideEdgePointBaseOffset_ += numPointsForInsideEdge[odd] - 1; 1689 } 1690 numPointsForOutsideEdge_[edge] = numPointsForInsideEdge[odd]; 1691 } 1692 } 1693 1694 // Triangulate center - a row of quads if odd 1695 // This triangulation may be producing diagonals that are asymmetric about 1696 // the center of the patch in this region. 1697 if ((numPointsForInsideTessFactor[U] > numPointsForInsideTessFactor[V]) && 1698 insideTessFactorOdd[V]) { 1699 ctx.bUsingPatchedIndices2 = true; 1700 int stripNumQuads = (((numPointsForInsideTessFactor[U] >> 1) - 1701 (numPointsForInsideTessFactor[V] >> 1)) 1702 << 1) + 1703 (insideTessFactorOdd[U] ? 1 : 2); 1704 ctx.IndexPatchCtx2.baseIndexToInvert = 1705 outsideEdgePointBaseOffset + stripNumQuads + 2; 1706 ctx.IndexPatchCtx2.cornerCaseBadValue = 1707 ctx.IndexPatchCtx2.baseIndexToInvert; 1708 ctx.IndexPatchCtx2.cornerCaseReplacementValue = 1709 outsideEdgePointBaseOffset; 1710 ctx.IndexPatchCtx2.indexInversionEndPoint = 1711 ctx.IndexPatchCtx2.baseIndexToInvert + 1712 ctx.IndexPatchCtx2.baseIndexToInvert + stripNumQuads; 1713 StitchRegular( 1714 &ctx, /*bTrapezoid*/ false, DIAGONALS_INSIDE_TO_OUTSIDE, 1715 /*baseIndexOffset: */ NumIndices, 1716 /*numInsideEdgePoints:*/ stripNumQuads + 1, 1717 /*insideEdgePointBaseOffset*/ ctx.IndexPatchCtx2.baseIndexToInvert, 1718 outsideEdgePointBaseOffset + 1); 1719 ctx.bUsingPatchedIndices2 = false; 1720 NumIndices += stripNumQuads * 6; 1721 } else if ((numPointsForInsideTessFactor[V] >= 1722 numPointsForInsideTessFactor[U]) && 1723 insideTessFactorOdd[U]) { 1724 ctx.bUsingPatchedIndices2 = true; 1725 int stripNumQuads = (((numPointsForInsideTessFactor[V] >> 1) - 1726 (numPointsForInsideTessFactor[U] >> 1)) 1727 << 1) + 1728 (insideTessFactorOdd[V] ? 1 : 2); 1729 ctx.IndexPatchCtx2.baseIndexToInvert = 1730 outsideEdgePointBaseOffset + stripNumQuads + 1; 1731 ctx.IndexPatchCtx2.cornerCaseBadValue = -1; // unused 1732 ctx.IndexPatchCtx2.indexInversionEndPoint = 1733 ctx.IndexPatchCtx2.baseIndexToInvert + 1734 ctx.IndexPatchCtx2.baseIndexToInvert + stripNumQuads; 1735 DIAGONALS diag = insideTessFactorOdd[V] 1736 ? DIAGONALS_INSIDE_TO_OUTSIDE_EXCEPT_MIDDLE 1737 : DIAGONALS_INSIDE_TO_OUTSIDE; 1738 StitchRegular( 1739 &ctx, /*bTrapezoid*/ false, diag, 1740 /*baseIndexOffset: */ NumIndices, 1741 /*numInsideEdgePoints:*/ stripNumQuads + 1, 1742 /*insideEdgePointBaseOffset*/ ctx.IndexPatchCtx2.baseIndexToInvert, 1743 outsideEdgePointBaseOffset); 1744 ctx.bUsingPatchedIndices2 = false; 1745 NumIndices += stripNumQuads * 6; 1746 } 1747 } 1748} 1749