xref: /aosp_15_r20/external/mesa3d/src/asahi/lib/shaders/tessellator.cl (revision 6104692788411f58d303aa86923a9ff6ecaded22)
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