1 /*
2     Copyright (c) Microsoft Corporation
3 
4     Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
5     associated documentation files (the "Software"), to deal in the Software without restriction,
6     including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
7     and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
8     subject to the following conditions:
9 
10     The above copyright notice and this permission notice shall be included in all copies or substantial
11     portions of the Software.
12 
13     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
14     NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
15     IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
16     WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
17     SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
18 */
19 
20 #include "tessellator.hpp"
21 #include "util/macros.h"
22 #if defined(_MSC_VER)
23 #include <math.h> // ceil
24 #else
25 #include <cmath>
26 #endif
27 #define min(x,y) (x < y ? x : y)
28 #define max(x,y) (x > y ? x : y)
29 
30 //=================================================================================================================================
31 // Some D3D Compliant Float Math (reference rasterizer implements these in RefALU class)
32 //=================================================================================================================================
33 //
34 //---------------------------------------------------------------------------------------------------------------------------------
35 // isNaN
36 //---------------------------------------------------------------------------------------------------------------------------------
37 
38 union fiu {
39     float f;
40     int i;
41 };
42 
tess_isNaN(float a)43 static bool tess_isNaN( float a )
44 {
45     static const int exponentMask = 0x7f800000;
46     static const int mantissaMask = 0x007fffff;
47     union fiu fiu;
48     fiu.f = a;
49     return ( ( ( fiu.i & exponentMask ) == exponentMask ) && ( fiu.i & mantissaMask ) ); // NaN
50 }
51 
52 //---------------------------------------------------------------------------------------------------------------------------------
53 // flush (denorm)
54 //---------------------------------------------------------------------------------------------------------------------------------
tess_flush(float a)55 static float tess_flush( float a )
56 {
57     static const int minNormalizedFloat = 0x00800000;
58     static const int signBit = 0x80000000;
59     static const int signBitComplement = 0x7fffffff;
60     union fiu fiu, uif;
61     fiu.f = a;
62     int b = fiu.i & signBitComplement; // fabs()
63     if( b < minNormalizedFloat ) // UINT comparison. NaN/INF do test false here
64     {
65         b = signBit & (fiu.i);
66         uif.i = b;
67         return uif.f;
68     }
69     return a;
70 }
71 
72 //---------------------------------------------------------------------------------------------------------------------------------
73 // IEEE754R min
74 //---------------------------------------------------------------------------------------------------------------------------------
tess_fmin(float a,float b)75 static float tess_fmin( float a, float b )
76 {
77     float _a = tess_flush( a );
78     float _b = tess_flush( b );
79     if( tess_isNaN( _b ) )
80     {
81         return a;
82     }
83     else if( ( _a == 0 ) && ( _b == 0 ) )
84     {
85         union fiu fiu;
86         fiu.f = _a;
87         return ( fiu.i & 0x80000000 ) ? a : b;
88     }
89     return _a < _b ? a : b;
90 }
91 
92 //---------------------------------------------------------------------------------------------------------------------------------
93 // IEEE754R max
94 //---------------------------------------------------------------------------------------------------------------------------------
tess_fmax(float a,float b)95 static float tess_fmax( float a, float b )
96 {
97     float _a = tess_flush( a );
98     float _b = tess_flush( b );
99 
100     if( tess_isNaN( _b ) )
101     {
102         return a;
103     }
104     else if( ( _a == 0 ) && ( _b == 0 ) )
105     {
106         union fiu fiu;
107         fiu.f = _b;
108         return ( fiu.i & 0x80000000 ) ? a : b;
109     }
110     return _a >= _b ? a : b;
111 }
112 
113 //=================================================================================================================================
114 // Fixed Point Math
115 //=================================================================================================================================
116 
117 //-----------------------------------------------------------------------------------------------------------------------------
118 // floatToFixedPoint
119 //
120 // Convert 32-bit float to 32-bit fixed point integer, using only
121 // integer arithmetic + bitwise operations.
122 //
123 // c_uIBits:  UINT8     : Width of i (aka. integer bits)
124 // c_uFBits:  UINT8     : Width of f (aka. fractional bits)
125 // c_bSigned: bool      : Whether the integer bits are a 2's complement signed value
126 // input:     float     : All values valid.
127 // output:    INT32     : At most 24 bits from LSB are meaningful, depending
128 //                        on the fixed point bit representation chosen (see
129 //                        below).  Extra bits are sign extended from the most
130 //                        meaningful bit.
131 //
132 //-----------------------------------------------------------------------------------------------------------------------------
133 
134 typedef unsigned char UINT8;
135 typedef int INT32;
136 template< const UINT8 c_uIBits, const UINT8 c_uFBits, const bool c_bSigned >
floatToIDotF(const float & input)137 INT32 floatToIDotF( const float& input )
138 {
139     // ------------------------------------------------------------------------
140     //                                                output fixed point format
141     // 32-bit result:
142     //
143     //      [sign-extend]i.f
144     //      |              |
145     //      MSB(31)...LSB(0)
146     //
147     //      f               fractional part of the number, an unsigned
148     //                      value with _fxpFracBitCount bits (defined below)
149     //
150     //      .               implied decimal
151     //
152     //      i               integer part of the number, a 2's complement
153     //                      value with _fxpIntBitCount bits (defined below)
154     //
155     //      [sign-extend]   MSB of i conditionally replicated
156     //
157     // ------------------------------------------------------------------------
158     // Define fixed point bit counts
159     //
160 
161     // Commenting out C_ASSERT below to minimise #includes:
162     // C_ASSERT( 2 <= c_uIBits && c_uIBits <= 32 && c_uFBits <= 32 && c_uIBits + c_uFBits <= 32 );
163 
164     // Define most negative and most positive fixed point values
165     const INT32 c_iMinResult = (c_bSigned ? INT32( -1 ) << (c_uIBits + c_uFBits - 1) : 0);
166     const INT32 c_iMaxResult = ~c_iMinResult;
167 
168     // ------------------------------------------------------------------------
169     //                                                constant float properties
170     // ------------------------------------------------------------------------
171     const UINT8 _fltMantissaBitCount = 23;
172     const UINT8 _fltExponentBitCount = 8;
173     const INT32 _fltExponentBias     = (INT32( 1 ) << (_fltExponentBitCount - 1)) - 1;
174     const INT32 _fltHiddenBit        = INT32( 1 ) << _fltMantissaBitCount;
175     const INT32 _fltMantissaMask     = _fltHiddenBit - 1;
176     const INT32 _fltExponentMask     = ((INT32( 1 ) << _fltExponentBitCount) - 1) << _fltMantissaBitCount;
177     const INT32 _fltSignBit          = INT32( 1 ) << (_fltExponentBitCount + _fltMantissaBitCount);
178 
179     // ------------------------------------------------------------------------
180     //              define min and max values as floats (clamp to these bounds)
181     // ------------------------------------------------------------------------
182     INT32 _fxpMaxPosValueFloat;
183     INT32 _fxpMaxNegValueFloat;
184 
185     if (c_bSigned)
186     {
187         // The maximum positive fixed point value is 2^(i-1) - 2^(-f).
188         // The following constructs the floating point bit pattern for this value,
189         // as long as i >= 2.
190         _fxpMaxPosValueFloat = (_fltExponentBias + c_uIBits - 1) <<_fltMantissaBitCount;
191         const INT32 iShift = _fltMantissaBitCount + 2 - c_uIBits - c_uFBits;
192         if (iShift >= 0)
193         {
194 //            assert( iShift < 32 );
195 #if defined(_MSC_VER)
196 #pragma warning( push )
197 #pragma warning( disable : 4293 26452 )
198 #endif
199             _fxpMaxPosValueFloat -= INT32( 1 ) << iShift;
200 #if defined(_MSC_VER)
201 #pragma warning( pop )
202 #endif
203         }
204 
205         // The maximum negative fixed point value is -2^(i-1).
206         // The following constructs the floating point bit pattern for this value,
207         // as long as i >= 2.
208         // We need this number without the sign bit
209         _fxpMaxNegValueFloat = (_fltExponentBias + c_uIBits - 1) << _fltMantissaBitCount;
210     }
211     else
212     {
213         // The maximum positive fixed point value is 2^(i) - 2^(-f).
214         // The following constructs the floating point bit pattern for this value,
215         // as long as i >= 2.
216         _fxpMaxPosValueFloat = (_fltExponentBias + c_uIBits) <<_fltMantissaBitCount;
217         const INT32 iShift = _fltMantissaBitCount + 1 - c_uIBits - c_uFBits;
218         if (iShift >= 0)
219         {
220 //            assert( iShift < 32 );
221 #if defined(_MSC_VER)
222 #pragma warning( push )
223 #pragma warning( disable : 4293 26452 )
224 #endif
225             _fxpMaxPosValueFloat -= INT32( 1 ) << iShift;
226 #if defined(_MSC_VER)
227 #pragma warning( pop )
228 #endif
229         }
230 
231         // The maximum negative fixed point value is 0.
232         _fxpMaxNegValueFloat = 0;
233     }
234 
235     // ------------------------------------------------------------------------
236     //                                                float -> fixed conversion
237     // ------------------------------------------------------------------------
238 
239     // ------------------------------------------------------------------------
240     //                                                      examine input float
241     // ------------------------------------------------------------------------
242     INT32 output              = *(INT32*)&input;
243     INT32 unbiasedExponent    = ((output & _fltExponentMask) >> _fltMantissaBitCount) - _fltExponentBias;
244     INT32 isNegative          = output & _fltSignBit;
245 
246     // ------------------------------------------------------------------------
247     //                                                                      nan
248     // ------------------------------------------------------------------------
249     if (unbiasedExponent == (_fltExponentBias + 1) && (output & _fltMantissaMask))
250     {
251         // nan converts to 0
252         output = 0;
253     }
254     // ------------------------------------------------------------------------
255     //                                                       too large positive
256     // ------------------------------------------------------------------------
257     else if (!isNegative && output >= _fxpMaxPosValueFloat) // integer compare
258     {
259         output = c_iMaxResult;
260     }
261     // ------------------------------------------------------------------------
262     //                                                       too large negative
263     // ------------------------------------------------------------------------
264                                             // integer compare
265     else if (isNegative && (output & ~_fltSignBit) >= _fxpMaxNegValueFloat)
266     {
267         output = c_iMinResult;
268     }
269     // ------------------------------------------------------------------------
270     //                                                                too small
271     // ------------------------------------------------------------------------
272     else if (unbiasedExponent < -c_uFBits - 1)
273     {
274         // clamp to 0
275         output = 0;
276     }
277     // ------------------------------------------------------------------------
278     //                                                             within range
279     // ------------------------------------------------------------------------
280     else
281     {
282         // copy mantissa, add hidden bit
283         output = (output & _fltMantissaMask) | _fltHiddenBit;
284 
285         INT32 extraBits = _fltMantissaBitCount - c_uFBits - unbiasedExponent;
286         if (extraBits >= 0)
287         {
288             // 2's complement if negative
289             if (isNegative)
290             {
291                 output = ~output + 1;
292             }
293 
294             // From the range checks that led here, it is known that
295             // unbiasedExponent < c_uIBits.  So, at most:
296             // (a) unbiasedExponent == c_uIBits - 1.
297             //
298             // From compile validation above, it is known that
299             // c_uIBits + c_uFBits <= _fltMantissaBitCount + 1).
300             // So, at minimum:
301             // (b) _fltMantissaBitCount == _fxtIntBitCount + c_uFBits - 1
302             //
303             // Substituting (a) and (b) into extraBits calculation above:
304             // extraBits >= (_fxtIntBitCount + c_uFBits - 1)
305             //              - c_uFBits - (c_uIBits - 1)
306             // extraBits >= 0
307             //
308             // Thus we only have to worry about shifting right by 0 or more
309             // bits to get the decimal to the right place, and never have
310             // to shift left.
311 
312             INT32 LSB             = 1 << extraBits; // last bit being kept
313             INT32 extraBitsMask   = LSB - 1;
314             INT32 half            = LSB >> 1; // round bias
315 
316             // round to nearest-even at LSB
317             if ((output & LSB) || (output & extraBitsMask) > half)
318             {
319                 output += half;
320             }
321 
322             // shift off the extra bits (sign extending)
323             output >>= extraBits;
324         }
325         else
326         {
327             output <<= -extraBits;
328 
329             // 2's complement if negative
330             if (isNegative)
331             {
332                 output = ~output + 1;
333             }
334         }
335     }
336     return output;
337 }
338 //-----------------------------------------------------------------------------------------------------------------------------
339 
340 #define FXP_INTEGER_BITS 15
341 #define FXP_FRACTION_BITS 16
342 #define FXP_FRACTION_MASK 0x0000ffff
343 #define FXP_INTEGER_MASK 0x7fff0000
344 #define FXP_THREE (3<<FXP_FRACTION_BITS)
345 #define FXP_ONE (1<<FXP_FRACTION_BITS)
346 #define FXP_ONE_THIRD 0x00005555
347 #define FXP_TWO_THIRDS 0x0000aaaa
348 #define FXP_ONE_HALF   0x00008000
349 
350 #define FXP_MAX_INPUT_TESS_FACTOR_BEFORE_TRIPLE_AVERAGE 0x55540000 // 1/3 of max fixed point number - 1.  Numbers less than
351                                                     // or equal to this allows avg. reduction on a tri patch
352                                                     // including rounding.
353 
354 #define FXP_MAX_INPUT_TESS_FACTOR_BEFORE_PAIR_AVERAGE 0x7FFF0000 // 1/2 of max fixed point number - 1.  Numbers less than
355                                                     // or equal to this allows avg. reduction on a quad patch
356                                                     // including rounding.
357 
358 static const FXP s_fixedReciprocal[PIPE_TESSELLATOR_MAX_TESSELLATION_FACTOR+1] =
359 {
360     0xffffffff, // 1/0 is the first entry (unused)
361     0x10000, 0x8000, 0x5555, 0x4000,
362     0x3333, 0x2aab, 0x2492, 0x2000,
363     0x1c72, 0x199a, 0x1746, 0x1555,
364     0x13b1, 0x1249, 0x1111, 0x1000,
365     0xf0f, 0xe39, 0xd79, 0xccd,
366     0xc31, 0xba3, 0xb21, 0xaab,
367     0xa3d, 0x9d9, 0x97b, 0x925,
368     0x8d4, 0x889, 0x842, 0x800,
369     0x7c2, 0x788, 0x750, 0x71c,
370     0x6eb, 0x6bd, 0x690, 0x666,
371     0x63e, 0x618, 0x5f4, 0x5d1,
372     0x5b0, 0x591, 0x572, 0x555,
373     0x539, 0x51f, 0x505, 0x4ec,
374     0x4d5, 0x4be, 0x4a8, 0x492,
375     0x47e, 0x46a, 0x457, 0x444,
376     0x432, 0x421, 0x410, 0x400, // 1/64 is the last entry
377 };
378 
379 #define FLOAT_THREE 3.0f
380 #define FLOAT_ONE 1.0f
381 
382 //---------------------------------------------------------------------------------------------------------------------------------
383 // floatToFixed
384 //---------------------------------------------------------------------------------------------------------------------------------
floatToFixed(const float & input)385 FXP floatToFixed(const float& input)
386 {
387     return floatToIDotF< FXP_INTEGER_BITS, FXP_FRACTION_BITS, /*bSigned*/false >( input );
388 }
389 
390 //---------------------------------------------------------------------------------------------------------------------------------
391 // fixedToFloat
392 //---------------------------------------------------------------------------------------------------------------------------------
fixedToFloat(const FXP & input)393 float fixedToFloat(const FXP& input)
394 {
395     // not worrying about denorm flushing the float operations (the DX spec behavior for div), since the numbers will not be that small during tessellation.
396     return ((float)(input>>FXP_FRACTION_BITS) + (float)(input&FXP_FRACTION_MASK)/(1<<FXP_FRACTION_BITS));
397 }
398 
399 //---------------------------------------------------------------------------------------------------------------------------------
400 // isEven
401 //---------------------------------------------------------------------------------------------------------------------------------
isEven(const float & input)402 bool isEven(const float& input)
403 {
404     return (((int)input) & 1) ? false : true;
405 }
406 
407 //---------------------------------------------------------------------------------------------------------------------------------
408 // fxpCeil
409 //---------------------------------------------------------------------------------------------------------------------------------
fxpCeil(const FXP & input)410 FXP fxpCeil(const FXP& input)
411 {
412     if( input & FXP_FRACTION_MASK )
413     {
414         return (input & FXP_INTEGER_MASK) + FXP_ONE;
415     }
416     return input;
417 }
418 
419 //---------------------------------------------------------------------------------------------------------------------------------
420 // fxpFloor
421 //---------------------------------------------------------------------------------------------------------------------------------
fxpFloor(const FXP & input)422 FXP fxpFloor(const FXP& input)
423 {
424     return (input & FXP_INTEGER_MASK);
425 }
426 
427 //=================================================================================================================================
428 // CHWTessellator
429 //=================================================================================================================================
430 
431 //---------------------------------------------------------------------------------------------------------------------------------
432 // CHWTessellator::CHWTessellator
433 //---------------------------------------------------------------------------------------------------------------------------------
CHWTessellator()434 CHWTessellator::CHWTessellator()
435 {
436     m_Point = 0;
437     m_Index = 0;
438     m_NumPoints = 0;
439     m_NumIndices = 0;
440     m_bUsingPatchedIndices = false;
441     m_bUsingPatchedIndices2 = false;
442 }
443 //---------------------------------------------------------------------------------------------------------------------------------
444 // CHWTessellator::~CHWTessellator
445 //---------------------------------------------------------------------------------------------------------------------------------
~CHWTessellator()446 CHWTessellator::~CHWTessellator()
447 {
448     delete [] m_Point;
449     delete [] m_Index;
450 }
451 
452 //---------------------------------------------------------------------------------------------------------------------------------
453 // CHWTessellator::Init
454 // User calls this.
455 //---------------------------------------------------------------------------------------------------------------------------------
Init(PIPE_TESSELLATOR_PARTITIONING partitioning,PIPE_TESSELLATOR_OUTPUT_PRIMITIVE outputPrimitive)456 void CHWTessellator::Init(
457     PIPE_TESSELLATOR_PARTITIONING       partitioning,
458     PIPE_TESSELLATOR_OUTPUT_PRIMITIVE   outputPrimitive)
459 {
460     if( 0 == m_Point )
461     {
462         m_Point = new DOMAIN_POINT[MAX_POINT_COUNT];
463     }
464     if( 0 == m_Index )
465     {
466         m_Index = new int[MAX_INDEX_COUNT];
467     }
468     m_partitioning = partitioning;
469     m_originalPartitioning = partitioning;
470     switch( partitioning )
471     {
472     case PIPE_TESSELLATOR_PARTITIONING_INTEGER:
473     default:
474         break;
475     case PIPE_TESSELLATOR_PARTITIONING_FRACTIONAL_ODD:
476         m_parity = TESSELLATOR_PARITY_ODD;
477         break;
478     case PIPE_TESSELLATOR_PARTITIONING_FRACTIONAL_EVEN:
479         m_parity = TESSELLATOR_PARITY_EVEN;
480         break;
481     }
482     m_originalParity = m_parity;
483     m_outputPrimitive = outputPrimitive;
484     m_NumPoints = 0;
485     m_NumIndices = 0;
486 }
487 //---------------------------------------------------------------------------------------------------------------------------------
488 // CHWTessellator::TessellateQuadDomain
489 // User calls this
490 //---------------------------------------------------------------------------------------------------------------------------------
TessellateQuadDomain(float tessFactor_Ueq0,float tessFactor_Veq0,float tessFactor_Ueq1,float tessFactor_Veq1,float insideTessFactor_U,float insideTessFactor_V)491 void CHWTessellator::TessellateQuadDomain( float tessFactor_Ueq0, float tessFactor_Veq0, float tessFactor_Ueq1, float tessFactor_Veq1,
492                                          float insideTessFactor_U, float insideTessFactor_V )
493 {
494     PROCESSED_TESS_FACTORS_QUAD processedTessFactors;
495     QuadProcessTessFactors(tessFactor_Ueq0,tessFactor_Veq0,tessFactor_Ueq1,tessFactor_Veq1,insideTessFactor_U,insideTessFactor_V,processedTessFactors);
496 
497     if( processedTessFactors.bPatchCulled )
498     {
499         m_NumPoints = 0;
500         m_NumIndices = 0;
501         return;
502     }
503     else if( processedTessFactors.bJustDoMinimumTessFactor )
504     {
505         DefinePoint(/*U*/0,/*V*/0,/*pointStorageOffset*/0);
506         DefinePoint(/*U*/FXP_ONE,/*V*/0,/*pointStorageOffset*/1);
507         DefinePoint(/*U*/FXP_ONE,/*V*/FXP_ONE,/*pointStorageOffset*/2);
508         DefinePoint(/*U*/0,/*V*/FXP_ONE,/*pointStorageOffset*/3);
509         m_NumPoints = 4;
510 
511         switch(m_outputPrimitive)
512         {
513         case PIPE_TESSELLATOR_OUTPUT_TRIANGLE_CW:
514         case PIPE_TESSELLATOR_OUTPUT_TRIANGLE_CCW:
515             // function orients them CCW if needed
516             DefineClockwiseTriangle(0,1,3,/*indexStorageOffset*/0);
517             DefineClockwiseTriangle(1,2,3,/*indexStorageOffset*/3);
518             m_NumIndices = 6;
519             break;
520         case PIPE_TESSELLATOR_OUTPUT_POINT:
521             DumpAllPoints();
522             break;
523         case PIPE_TESSELLATOR_OUTPUT_LINE:
524             DumpAllPointsAsInOrderLineList();
525             break;
526         }
527         return;
528     }
529 
530     QuadGeneratePoints(processedTessFactors);
531 
532     if( m_outputPrimitive == PIPE_TESSELLATOR_OUTPUT_POINT )
533     {
534         DumpAllPoints();
535         return;
536     }
537     if( m_outputPrimitive == PIPE_TESSELLATOR_OUTPUT_LINE )
538     {
539         DumpAllPointsAsInOrderLineList();
540         return;
541     }
542 
543     QuadGenerateConnectivity(processedTessFactors); // can be done in parallel to QuadGeneratePoints()
544 }
545 
546 //---------------------------------------------------------------------------------------------------------------------------------
547 // CHWTessellator::QuadProcessTessFactors
548 //---------------------------------------------------------------------------------------------------------------------------------
QuadProcessTessFactors(float tessFactor_Ueq0,float tessFactor_Veq0,float tessFactor_Ueq1,float tessFactor_Veq1,float insideTessFactor_U,float insideTessFactor_V,PROCESSED_TESS_FACTORS_QUAD & processedTessFactors)549 void CHWTessellator::QuadProcessTessFactors( float tessFactor_Ueq0, float tessFactor_Veq0, float tessFactor_Ueq1, float tessFactor_Veq1,
550                       float insideTessFactor_U, float insideTessFactor_V, PROCESSED_TESS_FACTORS_QUAD& processedTessFactors )
551 {
552     // Is the patch culled?
553     if( !(tessFactor_Ueq0 > 0) || // NaN will pass
554         !(tessFactor_Veq0 > 0) ||
555         !(tessFactor_Ueq1 > 0) ||
556         !(tessFactor_Veq1 > 0) )
557     {
558         processedTessFactors.bPatchCulled = true;
559         return;
560     }
561     else
562     {
563         processedTessFactors.bPatchCulled = false;
564     }
565 
566     // Clamp edge TessFactors
567     float lowerBound = 0.0, upperBound = 0.0;
568     switch(m_originalPartitioning)
569     {
570         case PIPE_TESSELLATOR_PARTITIONING_INTEGER:
571         case PIPE_TESSELLATOR_PARTITIONING_POW2: // don�t care about pow2 distinction for validation, just treat as integer
572             lowerBound = PIPE_TESSELLATOR_MIN_ODD_TESSELLATION_FACTOR;
573             upperBound = PIPE_TESSELLATOR_MAX_EVEN_TESSELLATION_FACTOR;
574             break;
575 
576         case PIPE_TESSELLATOR_PARTITIONING_FRACTIONAL_EVEN:
577             lowerBound = PIPE_TESSELLATOR_MIN_EVEN_TESSELLATION_FACTOR;
578             upperBound = PIPE_TESSELLATOR_MAX_EVEN_TESSELLATION_FACTOR;
579             break;
580 
581         case PIPE_TESSELLATOR_PARTITIONING_FRACTIONAL_ODD:
582             lowerBound = PIPE_TESSELLATOR_MIN_ODD_TESSELLATION_FACTOR;
583             upperBound = PIPE_TESSELLATOR_MAX_ODD_TESSELLATION_FACTOR;
584             break;
585     }
586 
587     tessFactor_Ueq0 = tess_fmin( upperBound, tess_fmax( lowerBound, tessFactor_Ueq0 ) );
588     tessFactor_Veq0 = tess_fmin( upperBound, tess_fmax( lowerBound, tessFactor_Veq0 ) );
589     tessFactor_Ueq1 = tess_fmin( upperBound, tess_fmax( lowerBound, tessFactor_Ueq1 ) );
590     tessFactor_Veq1 = tess_fmin( upperBound, tess_fmax( lowerBound, tessFactor_Veq1 ) );
591 
592     if( HWIntegerPartitioning()) // pow2 or integer, round to next int (hw doesn't care about pow2 distinction)
593     {
594         tessFactor_Ueq0 = ceil(tessFactor_Ueq0);
595         tessFactor_Veq0 = ceil(tessFactor_Veq0);
596         tessFactor_Ueq1 = ceil(tessFactor_Ueq1);
597         tessFactor_Veq1 = ceil(tessFactor_Veq1);
598     }
599 
600     // Clamp inside TessFactors
601     if(PIPE_TESSELLATOR_PARTITIONING_FRACTIONAL_ODD == m_originalPartitioning)
602     {
603 #define EPSILON 0.0000152587890625f // 2^(-16), min positive fixed point fraction
604 #define MIN_ODD_TESSFACTOR_PLUS_HALF_EPSILON (PIPE_TESSELLATOR_MIN_ODD_TESSELLATION_FACTOR + EPSILON/2)
605         // If any TessFactor will end up > 1 after floatToFixed conversion later,
606         // then force the inside TessFactors to be > 1 so there is a picture frame.
607         if( (tessFactor_Ueq0 > MIN_ODD_TESSFACTOR_PLUS_HALF_EPSILON) ||
608             (tessFactor_Veq0 > MIN_ODD_TESSFACTOR_PLUS_HALF_EPSILON) ||
609             (tessFactor_Ueq1 > MIN_ODD_TESSFACTOR_PLUS_HALF_EPSILON) ||
610             (tessFactor_Veq1 > MIN_ODD_TESSFACTOR_PLUS_HALF_EPSILON) ||
611             (insideTessFactor_U > MIN_ODD_TESSFACTOR_PLUS_HALF_EPSILON) ||
612             (insideTessFactor_V > MIN_ODD_TESSFACTOR_PLUS_HALF_EPSILON) )
613         {
614             // Force picture frame
615             lowerBound = PIPE_TESSELLATOR_MIN_ODD_TESSELLATION_FACTOR + EPSILON;
616         }
617     }
618 
619     insideTessFactor_U = tess_fmin( upperBound, tess_fmax( lowerBound, insideTessFactor_U ) );
620     insideTessFactor_V = tess_fmin( upperBound, tess_fmax( lowerBound, insideTessFactor_V ) );
621     // Note the above clamps map NaN to lowerBound
622 
623 
624     if( HWIntegerPartitioning()) // pow2 or integer, round to next int (hw doesn't care about pow2 distinction)
625     {
626         insideTessFactor_U = ceil(insideTessFactor_U);
627         insideTessFactor_V = ceil(insideTessFactor_V);
628     }
629 
630     // Reset our vertex and index buffers.  We have enough storage for the max tessFactor.
631     m_NumPoints = 0;
632     m_NumIndices = 0;
633 
634     // Process tessFactors
635     float outsideTessFactor[QUAD_EDGES] = {tessFactor_Ueq0, tessFactor_Veq0, tessFactor_Ueq1, tessFactor_Veq1};
636     float insideTessFactor[QUAD_AXES] = {insideTessFactor_U,insideTessFactor_V};
637     int edge, axis;
638     if( HWIntegerPartitioning() )
639     {
640         for( edge = 0; edge < QUAD_EDGES; edge++ )
641         {
642             int edgeEven = isEven(outsideTessFactor[edge]);
643             processedTessFactors.outsideTessFactorParity[edge] = edgeEven ? TESSELLATOR_PARITY_EVEN : TESSELLATOR_PARITY_ODD;
644         }
645         for( axis = 0; axis < QUAD_AXES; axis++ )
646         {
647             processedTessFactors.insideTessFactorParity[axis] =
648                 (isEven(insideTessFactor[axis]) || (FLOAT_ONE == insideTessFactor[axis]) )
649                 ? TESSELLATOR_PARITY_EVEN : TESSELLATOR_PARITY_ODD;
650         }
651     }
652     else
653     {
654         for( edge = 0; edge < QUAD_EDGES; edge++ )
655         {
656             processedTessFactors.outsideTessFactorParity[edge] = m_originalParity;
657         }
658         processedTessFactors.insideTessFactorParity[U] = processedTessFactors.insideTessFactorParity[V] = m_originalParity;
659     }
660 
661     // Save fixed point TessFactors
662     for( edge = 0; edge < QUAD_EDGES; edge++ )
663     {
664         processedTessFactors.outsideTessFactor[edge] = floatToFixed(outsideTessFactor[edge]);
665     }
666     for( axis = 0; axis < QUAD_AXES; axis++ )
667     {
668         processedTessFactors.insideTessFactor[axis] = floatToFixed(insideTessFactor[axis]);
669     }
670 
671     if( HWIntegerPartitioning() || Odd() )
672     {
673         // Special case if all TessFactors are 1
674         if( (FXP_ONE == processedTessFactors.insideTessFactor[U]) &&
675             (FXP_ONE == processedTessFactors.insideTessFactor[V]) &&
676             (FXP_ONE == processedTessFactors.outsideTessFactor[Ueq0]) &&
677             (FXP_ONE == processedTessFactors.outsideTessFactor[Veq0]) &&
678             (FXP_ONE == processedTessFactors.outsideTessFactor[Ueq1]) &&
679             (FXP_ONE == processedTessFactors.outsideTessFactor[Veq1]) )
680         {
681             processedTessFactors.bJustDoMinimumTessFactor = true;
682             return;
683         }
684     }
685     processedTessFactors.bJustDoMinimumTessFactor = false;
686 
687     // Compute TessFactor-specific metadata
688     for(int edge = 0; edge < QUAD_EDGES; edge++ )
689     {
690         SetTessellationParity(processedTessFactors.outsideTessFactorParity[edge]);
691         ComputeTessFactorContext(processedTessFactors.outsideTessFactor[edge], processedTessFactors.outsideTessFactorCtx[edge]);
692     }
693 
694     for(int axis = 0; axis < QUAD_AXES; axis++)
695     {
696         SetTessellationParity(processedTessFactors.insideTessFactorParity[axis]);
697         ComputeTessFactorContext(processedTessFactors.insideTessFactor[axis], processedTessFactors.insideTessFactorCtx[axis]);
698     }
699 
700     // Compute some initial data.
701 
702     // outside edge offsets and storage
703     for(int edge = 0; edge < QUAD_EDGES; edge++ )
704     {
705         SetTessellationParity(processedTessFactors.outsideTessFactorParity[edge]);
706         processedTessFactors.numPointsForOutsideEdge[edge] = NumPointsForTessFactor(processedTessFactors.outsideTessFactor[edge]);
707         m_NumPoints += processedTessFactors.numPointsForOutsideEdge[edge];
708     }
709     m_NumPoints -= 4;
710 
711     // inside edge offsets
712     for(int axis = 0; axis < QUAD_AXES; axis++)
713     {
714         SetTessellationParity(processedTessFactors.insideTessFactorParity[axis]);
715         processedTessFactors.numPointsForInsideTessFactor[axis] = NumPointsForTessFactor(processedTessFactors.insideTessFactor[axis]);
716         int pointCountMin = ( TESSELLATOR_PARITY_ODD == processedTessFactors.insideTessFactorParity[axis] ) ? 4 : 3;
717         // max() allows degenerate transition regions when inside TessFactor == 1
718         processedTessFactors.numPointsForInsideTessFactor[axis] = max(pointCountMin,processedTessFactors.numPointsForInsideTessFactor[axis]);
719     }
720 
721     processedTessFactors.insideEdgePointBaseOffset = m_NumPoints;
722 
723     // inside storage, including interior edges above
724     int numInteriorPoints = (processedTessFactors.numPointsForInsideTessFactor[U] - 2)*(processedTessFactors.numPointsForInsideTessFactor[V]-2);
725     m_NumPoints += numInteriorPoints;
726 }
727 
728 //---------------------------------------------------------------------------------------------------------------------------------
729 // CHWTessellator::QuadGeneratePoints
730 //---------------------------------------------------------------------------------------------------------------------------------
QuadGeneratePoints(const PROCESSED_TESS_FACTORS_QUAD & processedTessFactors)731 void CHWTessellator::QuadGeneratePoints( const PROCESSED_TESS_FACTORS_QUAD& processedTessFactors )
732 {
733     // Generate exterior ring edge points, clockwise from top-left
734     int pointOffset = 0;
735     int edge;
736     for(edge = 0; edge < QUAD_EDGES; edge++ )
737     {
738         int parity = edge&0x1;
739         int startPoint = 0;
740         int endPoint = processedTessFactors.numPointsForOutsideEdge[edge] - 1;
741         for(int p = startPoint; p < endPoint; p++,pointOffset++) // don't include end, since next edge starts with it.
742         {
743             FXP fxpParam;
744             int q = ((edge==1)||(edge==2)) ? p : endPoint - p; // reverse order
745             SetTessellationParity(processedTessFactors.outsideTessFactorParity[edge]);
746             PlacePointIn1D(processedTessFactors.outsideTessFactorCtx[edge],q,fxpParam);
747             if( parity )
748             {
749                 DefinePoint(/*U*/fxpParam,
750                             /*V*/(edge == 3) ? FXP_ONE : 0,
751                             /*pointStorageOffset*/pointOffset);
752             }
753             else
754             {
755                 DefinePoint(/*U*/(edge == 2) ? FXP_ONE : 0,
756                             /*V*/fxpParam,
757                             /*pointStorageOffset*/pointOffset);
758             }
759         }
760     }
761 
762     // Generate interior ring points, clockwise from (U==0,V==1) (bottom-left) spiralling toward center
763     static const int startRing = 1;
764     int minNumPointsForTessFactor = min(processedTessFactors.numPointsForInsideTessFactor[U],processedTessFactors.numPointsForInsideTessFactor[V]);
765     int numRings = (minNumPointsForTessFactor >> 1);  // note for even tess we aren't counting center point here.
766     for(int ring = startRing; ring < numRings; ring++)
767     {
768         int startPoint = ring;
769         int endPoint[QUAD_AXES] = {processedTessFactors.numPointsForInsideTessFactor[U] - 1 - startPoint,
770                                    processedTessFactors.numPointsForInsideTessFactor[V] - 1 - startPoint};
771 
772         for(edge = 0; edge < QUAD_EDGES; edge++ )
773         {
774             int parity[QUAD_AXES] = {edge&0x1,((edge+1)&0x1)};
775             int perpendicularAxisPoint = (edge < 2) ? startPoint : endPoint[parity[0]];
776             FXP fxpPerpParam;
777             SetTessellationParity(processedTessFactors.insideTessFactorParity[parity[0]]);
778             PlacePointIn1D(processedTessFactors.insideTessFactorCtx[parity[0]],perpendicularAxisPoint,fxpPerpParam);
779             SetTessellationParity(processedTessFactors.insideTessFactorParity[parity[1]]);
780             for(int p = startPoint; p < endPoint[parity[1]]; p++, pointOffset++) // don't include end: next edge starts with it.
781             {
782                 FXP fxpParam;
783                 int q = ((edge == 1)||(edge==2)) ? p : endPoint[parity[1]] - (p - startPoint);
784                 PlacePointIn1D(processedTessFactors.insideTessFactorCtx[parity[1]],q,fxpParam);
785                 if( parity[1] )
786                 {
787                     DefinePoint(/*U*/fxpPerpParam,
788                                 /*V*/fxpParam,
789                                 /*pointStorageOffset*/pointOffset);
790                 }
791                 else
792                 {
793                     DefinePoint(/*U*/fxpParam,
794                                 /*V*/fxpPerpParam,
795                                 /*pointStorageOffset*/pointOffset);
796                 }
797             }
798         }
799     }
800     // For even tessellation, the inner "ring" is degenerate - a row of points
801     if( (processedTessFactors.numPointsForInsideTessFactor[U] > processedTessFactors.numPointsForInsideTessFactor[V]) &&
802         (TESSELLATOR_PARITY_EVEN == processedTessFactors.insideTessFactorParity[V]) )
803     {
804         int startPoint = numRings;
805         int endPoint = processedTessFactors.numPointsForInsideTessFactor[U] - 1 - startPoint;
806         SetTessellationParity(processedTessFactors.insideTessFactorParity[U]);
807         for( int p = startPoint; p <= endPoint; p++, pointOffset++ )
808         {
809             FXP fxpParam;
810             PlacePointIn1D(processedTessFactors.insideTessFactorCtx[U],p,fxpParam);
811             DefinePoint(/*U*/fxpParam,
812                         /*V*/FXP_ONE_HALF, // middle
813                         /*pointStorageOffset*/pointOffset);
814         }
815     }
816     else if( (processedTessFactors.numPointsForInsideTessFactor[V] >= processedTessFactors.numPointsForInsideTessFactor[U]) &&
817              (TESSELLATOR_PARITY_EVEN == processedTessFactors.insideTessFactorParity[U]) )
818     {
819         int startPoint = numRings;
820         int endPoint;
821         FXP fxpParam;
822         endPoint = processedTessFactors.numPointsForInsideTessFactor[V] - 1 - startPoint;
823         SetTessellationParity(processedTessFactors.insideTessFactorParity[V]);
824         for( int p = endPoint; p >= startPoint; p--, pointOffset++ )
825         {
826             PlacePointIn1D(processedTessFactors.insideTessFactorCtx[V],p,fxpParam);
827             DefinePoint(/*U*/FXP_ONE_HALF, // middle
828                         /*V*/fxpParam,
829                         /*pointStorageOffset*/pointOffset);
830         }
831     }
832 }
833 //---------------------------------------------------------------------------------------------------------------------------------
834 // CHWTessellator::QuadGenerateConnectivity
835 //---------------------------------------------------------------------------------------------------------------------------------
QuadGenerateConnectivity(const PROCESSED_TESS_FACTORS_QUAD & processedTessFactors)836 void CHWTessellator::QuadGenerateConnectivity( const PROCESSED_TESS_FACTORS_QUAD& processedTessFactors )
837 {
838     // Generate primitives for all the concentric rings, one side at a time for each ring
839     static const int startRing = 1;
840     int numPointRowsToCenter[QUAD_AXES] = {((processedTessFactors.numPointsForInsideTessFactor[U]+1) >> 1),
841                                             ((processedTessFactors.numPointsForInsideTessFactor[V]+1) >> 1)}; // +1 is so even tess includes the center point
842     int numRings = min(numPointRowsToCenter[U],numPointRowsToCenter[V]);
843     int degeneratePointRing[QUAD_AXES] = { // Even partitioning causes degenerate row of points,
844                                            // which results in exceptions to the point ordering conventions
845                                            // when travelling around the rings counterclockwise.
846         (TESSELLATOR_PARITY_EVEN == processedTessFactors.insideTessFactorParity[V]) ? numPointRowsToCenter[V] - 1 : -1,
847         (TESSELLATOR_PARITY_EVEN == processedTessFactors.insideTessFactorParity[U]) ? numPointRowsToCenter[U] - 1 : -1 };
848 
849     const TESS_FACTOR_CONTEXT* outsideTessFactorCtx[QUAD_EDGES] = {&processedTessFactors.outsideTessFactorCtx[Ueq0],
850                                                     &processedTessFactors.outsideTessFactorCtx[Veq0],
851                                                     &processedTessFactors.outsideTessFactorCtx[Ueq1],
852                                                     &processedTessFactors.outsideTessFactorCtx[Veq1]};
853     TESSELLATOR_PARITY outsideTessFactorParity[QUAD_EDGES] = {processedTessFactors.outsideTessFactorParity[Ueq0],
854                                                         processedTessFactors.outsideTessFactorParity[Veq0],
855                                                         processedTessFactors.outsideTessFactorParity[Ueq1],
856                                                         processedTessFactors.outsideTessFactorParity[Veq1]};
857     int numPointsForOutsideEdge[QUAD_EDGES] = {processedTessFactors.numPointsForOutsideEdge[Ueq0],
858                                               processedTessFactors.numPointsForOutsideEdge[Veq0],
859                                               processedTessFactors.numPointsForOutsideEdge[Ueq1],
860                                               processedTessFactors.numPointsForOutsideEdge[Veq1]};
861 
862     int insideEdgePointBaseOffset = processedTessFactors.insideEdgePointBaseOffset;
863     int outsideEdgePointBaseOffset = 0;
864     int edge;
865     for(int ring = startRing; ring < numRings; ring++)
866     {
867         int numPointsForInsideEdge[QUAD_AXES] = {processedTessFactors.numPointsForInsideTessFactor[U] - 2*ring,
868                                                  processedTessFactors.numPointsForInsideTessFactor[V] - 2*ring};
869 
870         int edge0InsidePointBaseOffset = insideEdgePointBaseOffset;
871         int edge0OutsidePointBaseOffset = outsideEdgePointBaseOffset;
872 
873         for(edge = 0; edge < QUAD_EDGES; edge++ )
874         {
875             int parity = (edge+1)&0x1;
876 
877             int numTriangles = numPointsForInsideEdge[parity] + numPointsForOutsideEdge[edge] - 2;
878             int insideBaseOffset;
879             int outsideBaseOffset;
880             if( edge == 3 ) // We need to patch the indexing so Stitch() can think it sees
881                             // 2 sequentially increasing rows of points, even though we have wrapped around
882                             // to the end of the inner and outer ring's points, so the last point is really
883                             // the first point for the ring.
884                             // We make it so that when Stitch() calls AddIndex(), that function
885                             // will do any necessary index adjustment.
886             {
887                 if( ring == degeneratePointRing[parity] )
888                 {
889                     m_IndexPatchContext2.baseIndexToInvert = insideEdgePointBaseOffset + 1;
890                     m_IndexPatchContext2.cornerCaseBadValue = outsideEdgePointBaseOffset + numPointsForOutsideEdge[edge] - 1;
891                     m_IndexPatchContext2.cornerCaseReplacementValue = edge0OutsidePointBaseOffset;
892                     m_IndexPatchContext2.indexInversionEndPoint = (m_IndexPatchContext2.baseIndexToInvert << 1) - 1;
893                     insideBaseOffset = m_IndexPatchContext2.baseIndexToInvert;
894                     outsideBaseOffset = outsideEdgePointBaseOffset;
895                     SetUsingPatchedIndices2(true);
896                 }
897                 else
898                 {
899                     m_IndexPatchContext.insidePointIndexDeltaToRealValue    = insideEdgePointBaseOffset;
900                     m_IndexPatchContext.insidePointIndexBadValue            = numPointsForInsideEdge[parity] - 1;
901                     m_IndexPatchContext.insidePointIndexReplacementValue    = edge0InsidePointBaseOffset;
902                     m_IndexPatchContext.outsidePointIndexPatchBase          = m_IndexPatchContext.insidePointIndexBadValue+1; // past inside patched index range
903                     m_IndexPatchContext.outsidePointIndexDeltaToRealValue   = outsideEdgePointBaseOffset
904                                                                                 - m_IndexPatchContext.outsidePointIndexPatchBase;
905                     m_IndexPatchContext.outsidePointIndexBadValue           = m_IndexPatchContext.outsidePointIndexPatchBase
906                                                                                 + numPointsForOutsideEdge[edge] - 1;
907                     m_IndexPatchContext.outsidePointIndexReplacementValue   = edge0OutsidePointBaseOffset;
908 
909                     insideBaseOffset = 0;
910                     outsideBaseOffset = m_IndexPatchContext.outsidePointIndexPatchBase;
911                     SetUsingPatchedIndices(true);
912                 }
913             }
914             else if( (edge == 2) && (ring == degeneratePointRing[parity]) )
915             {
916                 m_IndexPatchContext2.baseIndexToInvert = insideEdgePointBaseOffset;
917                 m_IndexPatchContext2.cornerCaseBadValue = -1; // unused
918                 m_IndexPatchContext2.cornerCaseReplacementValue = -1; // unused
919                 m_IndexPatchContext2.indexInversionEndPoint = m_IndexPatchContext2.baseIndexToInvert << 1;
920                 insideBaseOffset = m_IndexPatchContext2.baseIndexToInvert;
921                 outsideBaseOffset = outsideEdgePointBaseOffset;
922                 SetUsingPatchedIndices2(true);
923             }
924             else
925             {
926                 insideBaseOffset = insideEdgePointBaseOffset;
927                 outsideBaseOffset = outsideEdgePointBaseOffset;
928             }
929             if( ring == startRing )
930             {
931                 StitchTransition(/*baseIndexOffset: */m_NumIndices,
932                                insideBaseOffset,processedTessFactors.insideTessFactorCtx[parity].numHalfTessFactorPoints,processedTessFactors.insideTessFactorParity[parity],
933                                outsideBaseOffset,outsideTessFactorCtx[edge]->numHalfTessFactorPoints,outsideTessFactorParity[edge]);
934             }
935             else
936             {
937                 StitchRegular(/*bTrapezoid*/true, DIAGONALS_MIRRORED,
938                               /*baseIndexOffset: */m_NumIndices,
939                               numPointsForInsideEdge[parity],
940                               insideBaseOffset,outsideBaseOffset);
941             }
942             SetUsingPatchedIndices(false);
943             SetUsingPatchedIndices2(false);
944             m_NumIndices += numTriangles*3;
945             outsideEdgePointBaseOffset += numPointsForOutsideEdge[edge] - 1;
946             if( (edge == 2) && (ring == degeneratePointRing[parity]) )
947             {
948                 insideEdgePointBaseOffset -= numPointsForInsideEdge[parity] - 1;
949             }
950             else
951             {
952                 insideEdgePointBaseOffset += numPointsForInsideEdge[parity] - 1;
953             }
954             numPointsForOutsideEdge[edge] = numPointsForInsideEdge[parity];
955         }
956         if( startRing == ring )
957         {
958             for(edge = 0; edge < QUAD_EDGES; edge++ )
959             {
960                 outsideTessFactorCtx[edge] = &processedTessFactors.insideTessFactorCtx[edge&1];
961                 outsideTessFactorParity[edge] = processedTessFactors.insideTessFactorParity[edge&1];
962             }
963         }
964     }
965 
966     // Triangulate center - a row of quads if odd
967     // This triangulation may be producing diagonals that are asymmetric about
968     // the center of the patch in this region.
969     if( (processedTessFactors.numPointsForInsideTessFactor[U] > processedTessFactors.numPointsForInsideTessFactor[V]) &&
970         (TESSELLATOR_PARITY_ODD == processedTessFactors.insideTessFactorParity[V] ) )
971     {
972         SetUsingPatchedIndices2(true);
973         int stripNumQuads = (((processedTessFactors.numPointsForInsideTessFactor[U]>>1) - (processedTessFactors.numPointsForInsideTessFactor[V]>>1))<<1)+
974                             ((TESSELLATOR_PARITY_EVEN == processedTessFactors.insideTessFactorParity[U] ) ? 2 : 1);
975         m_IndexPatchContext2.baseIndexToInvert = outsideEdgePointBaseOffset + stripNumQuads + 2;
976         m_IndexPatchContext2.cornerCaseBadValue = m_IndexPatchContext2.baseIndexToInvert;
977         m_IndexPatchContext2.cornerCaseReplacementValue = outsideEdgePointBaseOffset;
978         m_IndexPatchContext2.indexInversionEndPoint = m_IndexPatchContext2.baseIndexToInvert +
979                                                       m_IndexPatchContext2.baseIndexToInvert + stripNumQuads;
980         StitchRegular(/*bTrapezoid*/false,DIAGONALS_INSIDE_TO_OUTSIDE,
981                        /*baseIndexOffset: */m_NumIndices, /*numInsideEdgePoints:*/stripNumQuads+1,
982                        /*insideEdgePointBaseOffset*/m_IndexPatchContext2.baseIndexToInvert,
983                        outsideEdgePointBaseOffset+1);
984         SetUsingPatchedIndices2(false);
985         m_NumIndices += stripNumQuads*6;
986     }
987     else if((processedTessFactors.numPointsForInsideTessFactor[V] >= processedTessFactors.numPointsForInsideTessFactor[U]) &&
988             (TESSELLATOR_PARITY_ODD == processedTessFactors.insideTessFactorParity[U]) )
989     {
990         SetUsingPatchedIndices2(true);
991         int stripNumQuads = (((processedTessFactors.numPointsForInsideTessFactor[V]>>1) - (processedTessFactors.numPointsForInsideTessFactor[U]>>1))<<1)+
992                             ((TESSELLATOR_PARITY_EVEN == processedTessFactors.insideTessFactorParity[V] ) ? 2 : 1);
993         m_IndexPatchContext2.baseIndexToInvert = outsideEdgePointBaseOffset + stripNumQuads + 1;
994         m_IndexPatchContext2.cornerCaseBadValue = -1; // unused
995         m_IndexPatchContext2.indexInversionEndPoint = m_IndexPatchContext2.baseIndexToInvert +
996                                                       m_IndexPatchContext2.baseIndexToInvert + stripNumQuads;
997 		DIAGONALS diag = (TESSELLATOR_PARITY_EVEN == processedTessFactors.insideTessFactorParity[V]) ?
998 							DIAGONALS_INSIDE_TO_OUTSIDE : DIAGONALS_INSIDE_TO_OUTSIDE_EXCEPT_MIDDLE;
999         StitchRegular(/*bTrapezoid*/false,diag,
1000                        /*baseIndexOffset: */m_NumIndices, /*numInsideEdgePoints:*/stripNumQuads+1,
1001                        /*insideEdgePointBaseOffset*/m_IndexPatchContext2.baseIndexToInvert,
1002                        outsideEdgePointBaseOffset);
1003         SetUsingPatchedIndices2(false);
1004         m_NumIndices += stripNumQuads*6;
1005     }
1006 }
1007 
1008 //---------------------------------------------------------------------------------------------------------------------------------
1009 // CHWTessellator::TessellateTriDomain
1010 // User calls this
1011 //---------------------------------------------------------------------------------------------------------------------------------
TessellateTriDomain(float tessFactor_Ueq0,float tessFactor_Veq0,float tessFactor_Weq0,float insideTessFactor)1012 void CHWTessellator::TessellateTriDomain( float tessFactor_Ueq0, float tessFactor_Veq0, float tessFactor_Weq0,
1013                                         float insideTessFactor )
1014 {
1015     PROCESSED_TESS_FACTORS_TRI processedTessFactors;
1016     TriProcessTessFactors(tessFactor_Ueq0,tessFactor_Veq0,tessFactor_Weq0,insideTessFactor,processedTessFactors);
1017 
1018     if( processedTessFactors.bPatchCulled )
1019     {
1020         m_NumPoints = 0;
1021         m_NumIndices = 0;
1022         return;
1023     }
1024     else if( processedTessFactors.bJustDoMinimumTessFactor )
1025     {
1026         DefinePoint(/*U*/0,/*V*/FXP_ONE,/*pointStorageOffset*/0); //V=1 (beginning of Ueq0 edge VW)
1027         DefinePoint(/*U*/0,/*V*/0,/*pointStorageOffset*/1); //W=1 (beginning of Veq0 edge WU)
1028         DefinePoint(/*U*/FXP_ONE,/*V*/0,/*pointStorageOffset*/2); //U=1 (beginning of Weq0 edge UV)
1029         m_NumPoints = 3;
1030 
1031         switch(m_outputPrimitive)
1032         {
1033         case PIPE_TESSELLATOR_OUTPUT_TRIANGLE_CW:
1034         case PIPE_TESSELLATOR_OUTPUT_TRIANGLE_CCW:
1035             // function orients them CCW if needed
1036             DefineClockwiseTriangle(0,1,2,/*indexStorageBaseOffset*/m_NumIndices);
1037             m_NumIndices = 3;
1038             break;
1039         case PIPE_TESSELLATOR_OUTPUT_POINT:
1040             DumpAllPoints();
1041             break;
1042         case PIPE_TESSELLATOR_OUTPUT_LINE:
1043             DumpAllPointsAsInOrderLineList();
1044             break;
1045         }
1046         return;
1047     }
1048 
1049     TriGeneratePoints(processedTessFactors);
1050 
1051     if( m_outputPrimitive == PIPE_TESSELLATOR_OUTPUT_POINT )
1052     {
1053         DumpAllPoints();
1054         return;
1055     }
1056     if( m_outputPrimitive == PIPE_TESSELLATOR_OUTPUT_LINE )
1057     {
1058         DumpAllPointsAsInOrderLineList();
1059         return;
1060     }
1061 
1062     TriGenerateConnectivity(processedTessFactors); // can be done in parallel to TriGeneratePoints()
1063 }
1064 
1065 //---------------------------------------------------------------------------------------------------------------------------------
1066 // CHWTessellator::TriProcessTessFactors
1067 //---------------------------------------------------------------------------------------------------------------------------------
TriProcessTessFactors(float tessFactor_Ueq0,float tessFactor_Veq0,float tessFactor_Weq0,float insideTessFactor,PROCESSED_TESS_FACTORS_TRI & processedTessFactors)1068 void CHWTessellator::TriProcessTessFactors( float tessFactor_Ueq0, float tessFactor_Veq0, float tessFactor_Weq0,
1069                                             float insideTessFactor, PROCESSED_TESS_FACTORS_TRI& processedTessFactors )
1070 {
1071     // Is the patch culled?
1072     if( !(tessFactor_Ueq0 > 0) || // NaN will pass
1073         !(tessFactor_Veq0 > 0) ||
1074         !(tessFactor_Weq0 > 0) )
1075     {
1076         processedTessFactors.bPatchCulled = true;
1077         return;
1078     }
1079     else
1080     {
1081         processedTessFactors.bPatchCulled = false;
1082     }
1083 
1084     // Clamp edge TessFactors
1085     float lowerBound = 0.0, upperBound = 0.0;
1086     switch(m_originalPartitioning)
1087     {
1088         case PIPE_TESSELLATOR_PARTITIONING_INTEGER:
1089         case PIPE_TESSELLATOR_PARTITIONING_POW2: // don�t care about pow2 distinction for validation, just treat as integer
1090             lowerBound = PIPE_TESSELLATOR_MIN_ODD_TESSELLATION_FACTOR;
1091             upperBound = PIPE_TESSELLATOR_MAX_EVEN_TESSELLATION_FACTOR;
1092             break;
1093 
1094         case PIPE_TESSELLATOR_PARTITIONING_FRACTIONAL_EVEN:
1095             lowerBound = PIPE_TESSELLATOR_MIN_EVEN_TESSELLATION_FACTOR;
1096             upperBound = PIPE_TESSELLATOR_MAX_EVEN_TESSELLATION_FACTOR;
1097             break;
1098 
1099         case PIPE_TESSELLATOR_PARTITIONING_FRACTIONAL_ODD:
1100             lowerBound = PIPE_TESSELLATOR_MIN_ODD_TESSELLATION_FACTOR;
1101             upperBound = PIPE_TESSELLATOR_MAX_ODD_TESSELLATION_FACTOR;
1102             break;
1103     }
1104 
1105     tessFactor_Ueq0 = tess_fmin( upperBound, tess_fmax( lowerBound, tessFactor_Ueq0 ) );
1106     tessFactor_Veq0 = tess_fmin( upperBound, tess_fmax( lowerBound, tessFactor_Veq0 ) );
1107     tessFactor_Weq0 = tess_fmin( upperBound, tess_fmax( lowerBound, tessFactor_Weq0 ) );
1108 
1109     if( HWIntegerPartitioning()) // pow2 or integer, round to next int (hw doesn't care about pow2 distinction)
1110     {
1111         tessFactor_Ueq0 = ceil(tessFactor_Ueq0);
1112         tessFactor_Veq0 = ceil(tessFactor_Veq0);
1113         tessFactor_Weq0 = ceil(tessFactor_Weq0);
1114     }
1115 
1116     // Clamp inside TessFactors
1117     if(PIPE_TESSELLATOR_PARTITIONING_FRACTIONAL_ODD == m_originalPartitioning)
1118     {
1119         if( (tessFactor_Ueq0 > MIN_ODD_TESSFACTOR_PLUS_HALF_EPSILON) ||
1120             (tessFactor_Veq0 > MIN_ODD_TESSFACTOR_PLUS_HALF_EPSILON) ||
1121             (tessFactor_Weq0 > MIN_ODD_TESSFACTOR_PLUS_HALF_EPSILON))
1122             // Don't need the same check for insideTessFactor for tri patches,
1123             // since there is only one insideTessFactor, as opposed to quad
1124             // patches which have 2 insideTessFactors.
1125         {
1126             // Force picture frame
1127             lowerBound = PIPE_TESSELLATOR_MIN_ODD_TESSELLATION_FACTOR + EPSILON;
1128         }
1129     }
1130 
1131     insideTessFactor = tess_fmin( upperBound, tess_fmax( lowerBound, insideTessFactor ) );
1132     // Note the above clamps map NaN to lowerBound
1133 
1134     if( HWIntegerPartitioning()) // pow2 or integer, round to next int (hw doesn't care about pow2 distinction)
1135     {
1136         insideTessFactor = ceil(insideTessFactor);
1137     }
1138 
1139     // Reset our vertex and index buffers.  We have enough storage for the max tessFactor.
1140     m_NumPoints = 0;
1141     m_NumIndices = 0;
1142 
1143     // Process tessFactors
1144     float outsideTessFactor[TRI_EDGES] = {tessFactor_Ueq0, tessFactor_Veq0, tessFactor_Weq0};
1145     int edge;
1146     if( HWIntegerPartitioning() )
1147     {
1148         for( edge = 0; edge < TRI_EDGES; edge++ )
1149         {
1150             int edgeEven = isEven(outsideTessFactor[edge]);
1151             processedTessFactors.outsideTessFactorParity[edge] = edgeEven ? TESSELLATOR_PARITY_EVEN : TESSELLATOR_PARITY_ODD;
1152         }
1153         processedTessFactors.insideTessFactorParity = (isEven(insideTessFactor) || (FLOAT_ONE == insideTessFactor))
1154                                         ? TESSELLATOR_PARITY_EVEN : TESSELLATOR_PARITY_ODD;
1155     }
1156     else
1157     {
1158         for( edge = 0; edge < TRI_EDGES; edge++ )
1159         {
1160             processedTessFactors.outsideTessFactorParity[edge] = m_originalParity;
1161         }
1162         processedTessFactors.insideTessFactorParity = m_originalParity;
1163     }
1164 
1165     // Save fixed point TessFactors
1166     for( edge = 0; edge < TRI_EDGES; edge++ )
1167     {
1168         processedTessFactors.outsideTessFactor[edge] = floatToFixed(outsideTessFactor[edge]);
1169     }
1170     processedTessFactors.insideTessFactor = floatToFixed(insideTessFactor);
1171 
1172     if( HWIntegerPartitioning() || Odd() )
1173     {
1174         // Special case if all TessFactors are 1
1175         if( (FXP_ONE == processedTessFactors.insideTessFactor) &&
1176             (FXP_ONE == processedTessFactors.outsideTessFactor[Ueq0]) &&
1177             (FXP_ONE == processedTessFactors.outsideTessFactor[Veq0]) &&
1178             (FXP_ONE == processedTessFactors.outsideTessFactor[Weq0]) )
1179         {
1180             processedTessFactors.bJustDoMinimumTessFactor = true;
1181             return;
1182         }
1183     }
1184     processedTessFactors.bJustDoMinimumTessFactor = false;
1185 
1186     // Compute per-TessFactor metadata
1187     for(edge = 0; edge < TRI_EDGES; edge++ )
1188     {
1189         SetTessellationParity(processedTessFactors.outsideTessFactorParity[edge]);
1190         ComputeTessFactorContext(processedTessFactors.outsideTessFactor[edge], processedTessFactors.outsideTessFactorCtx[edge]);
1191     }
1192     SetTessellationParity(processedTessFactors.insideTessFactorParity);
1193     ComputeTessFactorContext(processedTessFactors.insideTessFactor, processedTessFactors.insideTessFactorCtx);
1194 
1195     // Compute some initial data.
1196 
1197     // outside edge offsets and storage
1198     for(edge = 0; edge < TRI_EDGES; edge++ )
1199     {
1200         SetTessellationParity(processedTessFactors.outsideTessFactorParity[edge]);
1201         processedTessFactors.numPointsForOutsideEdge[edge] = NumPointsForTessFactor(processedTessFactors.outsideTessFactor[edge]);
1202         m_NumPoints += processedTessFactors.numPointsForOutsideEdge[edge];
1203     }
1204     m_NumPoints -= 3;
1205 
1206     // inside edge offsets
1207     SetTessellationParity(processedTessFactors.insideTessFactorParity);
1208     processedTessFactors.numPointsForInsideTessFactor = NumPointsForTessFactor(processedTessFactors.insideTessFactor);
1209     {
1210         int pointCountMin = Odd() ? 4 : 3;
1211         // max() allows degenerate transition regions when inside TessFactor == 1
1212         processedTessFactors.numPointsForInsideTessFactor = max(pointCountMin,processedTessFactors.numPointsForInsideTessFactor);
1213     }
1214 
1215     processedTessFactors.insideEdgePointBaseOffset = m_NumPoints;
1216 
1217     // inside storage, including interior edges above
1218     {
1219         int numInteriorRings = (processedTessFactors.numPointsForInsideTessFactor >> 1) - 1;
1220         int numInteriorPoints;
1221         if( Odd() )
1222         {
1223             numInteriorPoints = TRI_EDGES*(numInteriorRings*(numInteriorRings+1) - numInteriorRings);
1224         }
1225         else
1226         {
1227             numInteriorPoints = TRI_EDGES*(numInteriorRings*(numInteriorRings+1)) + 1;
1228         }
1229         m_NumPoints += numInteriorPoints;
1230     }
1231 
1232 }
1233 
1234 //---------------------------------------------------------------------------------------------------------------------------------
1235 // CHWTessellator::TriGeneratePoints
1236 //---------------------------------------------------------------------------------------------------------------------------------
TriGeneratePoints(const PROCESSED_TESS_FACTORS_TRI & processedTessFactors)1237 void CHWTessellator::TriGeneratePoints( const PROCESSED_TESS_FACTORS_TRI& processedTessFactors )
1238 {
1239     // Generate exterior ring edge points, clockwise starting from point V (VW, the U==0 edge)
1240     int pointOffset = 0;
1241     int edge;
1242     for(edge = 0; edge < TRI_EDGES; edge++ )
1243     {
1244         int parity = edge&0x1;
1245         int startPoint = 0;
1246         int endPoint = processedTessFactors.numPointsForOutsideEdge[edge] - 1;
1247         for(int p = startPoint; p < endPoint; p++, pointOffset++) // don't include end, since next edge starts with it.
1248         {
1249             FXP fxpParam;
1250             int q = (parity) ? p : endPoint - p; // whether to reverse point order given we are defining V or U (W implicit):
1251                                                  // edge0, VW, has V decreasing, so reverse 1D points below
1252                                                  // edge1, WU, has U increasing, so don't reverse 1D points  below
1253                                                  // edge2, UV, has U decreasing, so reverse 1D points below
1254             SetTessellationParity(processedTessFactors.outsideTessFactorParity[edge]);
1255             PlacePointIn1D(processedTessFactors.outsideTessFactorCtx[edge],q,fxpParam);
1256             if( edge == 0 )
1257             {
1258                 DefinePoint(/*U*/0,
1259                             /*V*/fxpParam,
1260                             /*pointStorageOffset*/pointOffset);
1261             }
1262             else
1263             {
1264                 DefinePoint(/*U*/fxpParam,
1265                             /*V*/(edge == 2) ? FXP_ONE - fxpParam : 0,
1266                             /*pointStorageOffset*/pointOffset);
1267             }
1268         }
1269     }
1270 
1271     // Generate interior ring points, clockwise spiralling in
1272     SetTessellationParity(processedTessFactors.insideTessFactorParity);
1273     static const int startRing = 1;
1274     int numRings = (processedTessFactors.numPointsForInsideTessFactor >> 1);
1275     for(int ring = startRing; ring < numRings; ring++)
1276     {
1277         int startPoint = ring;
1278         int endPoint = processedTessFactors.numPointsForInsideTessFactor - 1 - startPoint;
1279 
1280         for(edge = 0; edge < TRI_EDGES; edge++ )
1281         {
1282             int parity = edge&0x1;
1283             int perpendicularAxisPoint = startPoint;
1284             FXP fxpPerpParam;
1285             PlacePointIn1D(processedTessFactors.insideTessFactorCtx,perpendicularAxisPoint,fxpPerpParam);
1286             fxpPerpParam *= FXP_TWO_THIRDS; // Map location to the right size in barycentric space.
1287                                          // I (amarp) can draw a picture to explain.
1288                                          // We know this fixed point math won't over/underflow
1289             fxpPerpParam = (fxpPerpParam+FXP_ONE_HALF/*round*/)>>FXP_FRACTION_BITS; // get back to n.16
1290             for(int p = startPoint; p < endPoint; p++, pointOffset++) // don't include end: next edge starts with it.
1291             {
1292                 FXP fxpParam;
1293                 int q = (parity) ? p : endPoint - (p - startPoint); // whether to reverse point given we are defining V or U (W implicit):
1294                                                          // edge0, VW, has V decreasing, so reverse 1D points below
1295                                                          // edge1, WU, has U increasing, so don't reverse 1D points  below
1296                                                          // edge2, UV, has U decreasing, so reverse 1D points below
1297                 PlacePointIn1D(processedTessFactors.insideTessFactorCtx,q,fxpParam);
1298                 // edge0 VW, has perpendicular parameter U constant
1299                 // edge1 WU, has perpendicular parameter V constant
1300                 // edge2 UV, has perpendicular parameter W constant
1301                 const unsigned int deriv = 2; // reciprocal is the rate of change of edge-parallel parameters as they are pushed into the triangle
1302                 switch(edge)
1303                 {
1304                 case 0:
1305                     DefinePoint(/*U*/fxpPerpParam,
1306                                 /*V*/fxpParam - (fxpPerpParam+1/*round*/)/deriv, // we know this fixed point math won't over/underflow
1307                                 /*pointStorageOffset*/pointOffset);
1308                     break;
1309                 case 1:
1310                     DefinePoint(/*U*/fxpParam - (fxpPerpParam+1/*round*/)/deriv,// we know this fixed point math won't over/underflow
1311                                 /*V*/fxpPerpParam,
1312                                 /*pointStorageOffset*/pointOffset);
1313                     break;
1314                 case 2:
1315                     DefinePoint(/*U*/fxpParam - (fxpPerpParam+1/*round*/)/deriv,// we know this fixed point math won't over/underflow
1316                                 /*V*/FXP_ONE - (fxpParam - (fxpPerpParam+1/*round*/)/deriv) - fxpPerpParam,// we know this fixed point math won't over/underflow
1317                                 /*pointStorageOffset*/pointOffset);
1318                     break;
1319                 }
1320             }
1321         }
1322     }
1323     if( !Odd() )
1324     {
1325         // Last point is the point at the center.
1326         DefinePoint(/*U*/FXP_ONE_THIRD,
1327                     /*V*/FXP_ONE_THIRD,
1328                     /*pointStorageOffset*/pointOffset);
1329     }
1330 }
1331 //---------------------------------------------------------------------------------------------------------------------------------
1332 // CHWTessellator::TriGenerateConnectivity
1333 //---------------------------------------------------------------------------------------------------------------------------------
TriGenerateConnectivity(const PROCESSED_TESS_FACTORS_TRI & processedTessFactors)1334 void CHWTessellator::TriGenerateConnectivity( const PROCESSED_TESS_FACTORS_TRI& processedTessFactors )
1335 {
1336     // Generate primitives for all the concentric rings, one side at a time for each ring
1337     static const int startRing = 1;
1338     int numRings = ((processedTessFactors.numPointsForInsideTessFactor+1) >> 1); // +1 is so even tess includes the center point, which we want to now
1339     const TESS_FACTOR_CONTEXT* outsideTessFactorCtx[TRI_EDGES] = {&processedTessFactors.outsideTessFactorCtx[Ueq0],
1340                                             &processedTessFactors.outsideTessFactorCtx[Veq0],
1341                                             &processedTessFactors.outsideTessFactorCtx[Weq0]};
1342     TESSELLATOR_PARITY outsideTessFactorParity[TRI_EDGES] = {processedTessFactors.outsideTessFactorParity[Ueq0],
1343                                             processedTessFactors.outsideTessFactorParity[Veq0],
1344                                             processedTessFactors.outsideTessFactorParity[Weq0]};
1345     int numPointsForOutsideEdge[TRI_EDGES] = {processedTessFactors.numPointsForOutsideEdge[Ueq0],
1346                                               processedTessFactors.numPointsForOutsideEdge[Veq0],
1347                                               processedTessFactors.numPointsForOutsideEdge[Weq0]};
1348 
1349     int insideEdgePointBaseOffset = processedTessFactors.insideEdgePointBaseOffset;
1350     int outsideEdgePointBaseOffset = 0;
1351     int edge;
1352     for(int ring = startRing; ring < numRings; ring++)
1353     {
1354         int numPointsForInsideEdge = processedTessFactors.numPointsForInsideTessFactor - 2*ring;
1355         int edge0InsidePointBaseOffset = insideEdgePointBaseOffset;
1356         int edge0OutsidePointBaseOffset = outsideEdgePointBaseOffset;
1357         for(edge = 0; edge < TRI_EDGES; edge++ )
1358         {
1359             int numTriangles = numPointsForInsideEdge + numPointsForOutsideEdge[edge] - 2;
1360 
1361             int insideBaseOffset;
1362             int outsideBaseOffset;
1363             if( edge == 2 )
1364             {
1365                 m_IndexPatchContext.insidePointIndexDeltaToRealValue    = insideEdgePointBaseOffset;
1366                 m_IndexPatchContext.insidePointIndexBadValue            = numPointsForInsideEdge - 1;
1367                 m_IndexPatchContext.insidePointIndexReplacementValue    = edge0InsidePointBaseOffset;
1368                 m_IndexPatchContext.outsidePointIndexPatchBase          = m_IndexPatchContext.insidePointIndexBadValue+1; // past inside patched index range
1369                 m_IndexPatchContext.outsidePointIndexDeltaToRealValue   = outsideEdgePointBaseOffset
1370                                                                             - m_IndexPatchContext.outsidePointIndexPatchBase;
1371                 m_IndexPatchContext.outsidePointIndexBadValue           = m_IndexPatchContext.outsidePointIndexPatchBase
1372                                                                             + numPointsForOutsideEdge[edge] - 1;
1373                 m_IndexPatchContext.outsidePointIndexReplacementValue   = edge0OutsidePointBaseOffset;
1374                 SetUsingPatchedIndices(true);
1375                 insideBaseOffset = 0;
1376                 outsideBaseOffset = m_IndexPatchContext.outsidePointIndexPatchBase;
1377             }
1378             else
1379             {
1380                 insideBaseOffset = insideEdgePointBaseOffset;
1381                 outsideBaseOffset = outsideEdgePointBaseOffset;
1382             }
1383             if( ring == startRing )
1384             {
1385                 StitchTransition(/*baseIndexOffset: */m_NumIndices,
1386                                insideBaseOffset,processedTessFactors.insideTessFactorCtx.numHalfTessFactorPoints,processedTessFactors.insideTessFactorParity,
1387                                outsideBaseOffset,outsideTessFactorCtx[edge]->numHalfTessFactorPoints,outsideTessFactorParity[edge]);
1388             }
1389             else
1390             {
1391                 StitchRegular(/*bTrapezoid*/true, DIAGONALS_MIRRORED,
1392                               /*baseIndexOffset: */m_NumIndices,
1393                               numPointsForInsideEdge,
1394                               insideBaseOffset,outsideBaseOffset);
1395             }
1396             if( 2 == edge )
1397             {
1398                 SetUsingPatchedIndices(false);
1399             }
1400             m_NumIndices += numTriangles*3;
1401             outsideEdgePointBaseOffset += numPointsForOutsideEdge[edge] - 1;
1402             insideEdgePointBaseOffset += numPointsForInsideEdge - 1;
1403             numPointsForOutsideEdge[edge] = numPointsForInsideEdge;
1404         }
1405         if( startRing == ring )
1406         {
1407             for(edge = 0; edge < TRI_EDGES; edge++ )
1408             {
1409                 outsideTessFactorCtx[edge] = &processedTessFactors.insideTessFactorCtx;
1410                 outsideTessFactorParity[edge] = processedTessFactors.insideTessFactorParity;
1411             }
1412         }
1413     }
1414     if( Odd() )
1415     {
1416         // Triangulate center (a single triangle)
1417         DefineClockwiseTriangle(outsideEdgePointBaseOffset, outsideEdgePointBaseOffset+1, outsideEdgePointBaseOffset+2,
1418                        m_NumIndices);
1419         m_NumIndices += 3;
1420     }
1421 }
1422 
1423 //---------------------------------------------------------------------------------------------------------------------------------
1424 // CHWTessellator::TessellateIsoLineDomain
1425 // User calls this.
1426 //---------------------------------------------------------------------------------------------------------------------------------
TessellateIsoLineDomain(float TessFactor_V_LineDensity,float TessFactor_U_LineDetail)1427 void CHWTessellator::TessellateIsoLineDomain( float TessFactor_V_LineDensity, float TessFactor_U_LineDetail )
1428 {
1429     PROCESSED_TESS_FACTORS_ISOLINE processedTessFactors;
1430     IsoLineProcessTessFactors(TessFactor_V_LineDensity,TessFactor_U_LineDetail,processedTessFactors);
1431     if( processedTessFactors.bPatchCulled )
1432     {
1433         m_NumPoints = 0;
1434         m_NumIndices = 0;
1435         return;
1436     }
1437     IsoLineGeneratePoints(processedTessFactors);
1438     IsoLineGenerateConnectivity(processedTessFactors); // can be done in parallel to IsoLineGeneratePoints
1439 }
1440 
1441 //---------------------------------------------------------------------------------------------------------------------------------
1442 // CHWTessellator::IsoLineProcessTessFactors
1443 //---------------------------------------------------------------------------------------------------------------------------------
IsoLineProcessTessFactors(float TessFactor_V_LineDensity,float TessFactor_U_LineDetail,PROCESSED_TESS_FACTORS_ISOLINE & processedTessFactors)1444 void CHWTessellator::IsoLineProcessTessFactors( float TessFactor_V_LineDensity, float TessFactor_U_LineDetail,
1445                                                 PROCESSED_TESS_FACTORS_ISOLINE& processedTessFactors )
1446 {
1447     // Is the patch culled?
1448     if( !(TessFactor_V_LineDensity > 0) || // NaN will pass
1449         !(TessFactor_U_LineDetail > 0) )
1450     {
1451         processedTessFactors.bPatchCulled = true;
1452         return;
1453     }
1454     else
1455     {
1456         processedTessFactors.bPatchCulled = false;
1457     }
1458 
1459     // Clamp edge TessFactors
1460     float lowerBound = 0.0, upperBound = 0.0;
1461     switch(m_originalPartitioning)
1462     {
1463         case PIPE_TESSELLATOR_PARTITIONING_INTEGER:
1464         case PIPE_TESSELLATOR_PARTITIONING_POW2: // don�t care about pow2 distinction for validation, just treat as integer
1465             lowerBound = PIPE_TESSELLATOR_MIN_ODD_TESSELLATION_FACTOR;
1466             upperBound = PIPE_TESSELLATOR_MAX_EVEN_TESSELLATION_FACTOR;
1467             break;
1468 
1469         case PIPE_TESSELLATOR_PARTITIONING_FRACTIONAL_EVEN:
1470             lowerBound = PIPE_TESSELLATOR_MIN_EVEN_TESSELLATION_FACTOR;
1471             upperBound = PIPE_TESSELLATOR_MAX_EVEN_TESSELLATION_FACTOR;
1472             break;
1473 
1474         case PIPE_TESSELLATOR_PARTITIONING_FRACTIONAL_ODD:
1475             lowerBound = PIPE_TESSELLATOR_MIN_ODD_TESSELLATION_FACTOR;
1476             upperBound = PIPE_TESSELLATOR_MAX_ODD_TESSELLATION_FACTOR;
1477             break;
1478     }
1479 
1480     TessFactor_V_LineDensity = tess_fmin( PIPE_TESSELLATOR_MAX_ISOLINE_DENSITY_TESSELLATION_FACTOR,
1481                                     tess_fmax( PIPE_TESSELLATOR_MIN_ISOLINE_DENSITY_TESSELLATION_FACTOR, TessFactor_V_LineDensity ) );
1482     TessFactor_U_LineDetail = tess_fmin( upperBound, tess_fmax( lowerBound, TessFactor_U_LineDetail ) );
1483 
1484     // Reset our vertex and index buffers.  We have enough storage for the max tessFactor.
1485     m_NumPoints = 0;
1486     m_NumIndices = 0;
1487 
1488     // Process tessFactors
1489     if( HWIntegerPartitioning() )
1490     {
1491         TessFactor_U_LineDetail = ceil(TessFactor_U_LineDetail);
1492         processedTessFactors.lineDetailParity = isEven(TessFactor_U_LineDetail) ? TESSELLATOR_PARITY_EVEN : TESSELLATOR_PARITY_ODD;
1493     }
1494     else
1495     {
1496         processedTessFactors.lineDetailParity = m_originalParity;
1497     }
1498 
1499     FXP fxpTessFactor_U_LineDetail = floatToFixed(TessFactor_U_LineDetail);
1500 
1501     SetTessellationParity(processedTessFactors.lineDetailParity);
1502 
1503     ComputeTessFactorContext(fxpTessFactor_U_LineDetail, processedTessFactors.lineDetailTessFactorCtx);
1504     processedTessFactors.numPointsPerLine = NumPointsForTessFactor(fxpTessFactor_U_LineDetail);
1505 
1506     OverridePartitioning(PIPE_TESSELLATOR_PARTITIONING_INTEGER);
1507 
1508     TessFactor_V_LineDensity = ceil(TessFactor_V_LineDensity);
1509     processedTessFactors.lineDensityParity = isEven(TessFactor_V_LineDensity) ? TESSELLATOR_PARITY_EVEN : TESSELLATOR_PARITY_ODD;
1510     SetTessellationParity(processedTessFactors.lineDensityParity);
1511     FXP fxpTessFactor_V_LineDensity = floatToFixed(TessFactor_V_LineDensity);
1512     ComputeTessFactorContext(fxpTessFactor_V_LineDensity, processedTessFactors.lineDensityTessFactorCtx);
1513 
1514     processedTessFactors.numLines = NumPointsForTessFactor(fxpTessFactor_V_LineDensity) - 1; // don't draw last line at V == 1.
1515 
1516     RestorePartitioning();
1517 
1518     // Compute some initial data.
1519 
1520     // outside edge offsets
1521     m_NumPoints = processedTessFactors.numPointsPerLine * processedTessFactors.numLines;
1522     if( m_outputPrimitive == PIPE_TESSELLATOR_OUTPUT_POINT )
1523     {
1524         m_NumIndices = m_NumPoints;
1525     }
1526     else // line
1527     {
1528         m_NumIndices = processedTessFactors.numLines*(processedTessFactors.numPointsPerLine-1)*2;
1529     }
1530 }
1531 
1532 //---------------------------------------------------------------------------------------------------------------------------------
1533 // CHWTessellator::IsoLineGeneratePoints
1534 //---------------------------------------------------------------------------------------------------------------------------------
IsoLineGeneratePoints(const PROCESSED_TESS_FACTORS_ISOLINE & processedTessFactors)1535 void CHWTessellator::IsoLineGeneratePoints( const PROCESSED_TESS_FACTORS_ISOLINE& processedTessFactors )
1536 {
1537     int line, pointOffset;
1538     for(line = 0, pointOffset = 0; line < processedTessFactors.numLines; line++)
1539     {
1540         for(int point = 0; point < processedTessFactors.numPointsPerLine; point++)
1541         {
1542             FXP fxpU,fxpV;
1543             SetTessellationParity(processedTessFactors.lineDensityParity);
1544             PlacePointIn1D(processedTessFactors.lineDensityTessFactorCtx,line,fxpV);
1545 
1546             SetTessellationParity(processedTessFactors.lineDetailParity);
1547             PlacePointIn1D(processedTessFactors.lineDetailTessFactorCtx,point,fxpU);
1548 
1549             DefinePoint(fxpU,fxpV,pointOffset++);
1550         }
1551     }
1552 }
1553 
1554 //---------------------------------------------------------------------------------------------------------------------------------
1555 // CHWTessellator::IsoLineGenerateConnectivity
1556 //---------------------------------------------------------------------------------------------------------------------------------
IsoLineGenerateConnectivity(const PROCESSED_TESS_FACTORS_ISOLINE & processedTessFactors)1557 void CHWTessellator::IsoLineGenerateConnectivity( const PROCESSED_TESS_FACTORS_ISOLINE& processedTessFactors )
1558 {
1559     int line, pointOffset, indexOffset;
1560     if( m_outputPrimitive == PIPE_TESSELLATOR_OUTPUT_POINT )
1561     {
1562         for(line = 0, pointOffset = 0, indexOffset = 0; line < processedTessFactors.numLines; line++)
1563         {
1564             for(int point = 0; point < processedTessFactors.numPointsPerLine; point++)
1565             {
1566                 DefineIndex(pointOffset++,indexOffset++);
1567             }
1568         }
1569     }
1570     else // line
1571     {
1572         for(line = 0, pointOffset = 0, indexOffset = 0; line < processedTessFactors.numLines; line++)
1573         {
1574             for(int point = 0; point < processedTessFactors.numPointsPerLine; point++)
1575             {
1576                 if( point > 0 )
1577                 {
1578                     DefineIndex(pointOffset-1,indexOffset++);
1579                     DefineIndex(pointOffset,indexOffset++);
1580                 }
1581                 pointOffset++;
1582             }
1583         }
1584     }
1585 }
1586 
1587 //---------------------------------------------------------------------------------------------------------------------------------
1588 // CHWTessellator::GetPointCount
1589 // User calls this.
1590 //---------------------------------------------------------------------------------------------------------------------------------
GetPointCount()1591 int CHWTessellator::GetPointCount()
1592 {
1593     return m_NumPoints;
1594 }
1595 
1596 //---------------------------------------------------------------------------------------------------------------------------------
1597 // CHWTessellator::GetIndexCount()
1598 // User calls this.
1599 //---------------------------------------------------------------------------------------------------------------------------------
GetIndexCount()1600 int CHWTessellator::GetIndexCount()
1601 {
1602     return m_NumIndices;
1603 }
1604 
1605 //---------------------------------------------------------------------------------------------------------------------------------
1606 // CHWTessellator::GetPoints()
1607 // User calls this.
1608 //---------------------------------------------------------------------------------------------------------------------------------
GetPoints()1609 DOMAIN_POINT* CHWTessellator::GetPoints()
1610 {
1611     return m_Point;
1612 }
1613 //---------------------------------------------------------------------------------------------------------------------------------
1614 // CHWTessellator::GetIndices()
1615 // User calls this.
1616 //---------------------------------------------------------------------------------------------------------------------------------
GetIndices()1617 int* CHWTessellator::GetIndices()
1618 {
1619     return m_Index;
1620 }
1621 
1622 //---------------------------------------------------------------------------------------------------------------------------------
1623 // CHWTessellator::DefinePoint()
1624 //---------------------------------------------------------------------------------------------------------------------------------
DefinePoint(FXP fxpU,FXP fxpV,int pointStorageOffset)1625 int CHWTessellator::DefinePoint(FXP fxpU, FXP fxpV, int pointStorageOffset)
1626 {
1627 //    WCHAR foo[80];
1628 //    StringCchPrintf(foo,80,L"off:%d, uv=(%f,%f)\n",pointStorageOffset,fixedToFloat(fxpU),fixedToFloat(fxpV));
1629 //    OutputDebugString(foo);
1630     m_Point[pointStorageOffset].u = fixedToFloat(fxpU);
1631     m_Point[pointStorageOffset].v = fixedToFloat(fxpV);
1632     return pointStorageOffset;
1633 }
1634 
1635 //---------------------------------------------------------------------------------------------------------------------------------
1636 // CHWTessellator::DefineIndex()
1637 //--------------------------------------------------------------------------------------------------------------------------------
DefineIndex(int index,int indexStorageOffset)1638 void CHWTessellator::DefineIndex(int index, int indexStorageOffset)
1639 {
1640     index = PatchIndexValue(index);
1641 //    WCHAR foo[80];
1642 //    StringCchPrintf(foo,80,L"off:%d, idx=%d, uv=(%f,%f)\n",indexStorageOffset,index,m_Point[index].u,m_Point[index].v);
1643 //    OutputDebugString(foo);
1644     m_Index[indexStorageOffset] = index;
1645 }
1646 
1647 //---------------------------------------------------------------------------------------------------------------------------------
1648 // CHWTessellator::DefineClockwiseTriangle()
1649 //---------------------------------------------------------------------------------------------------------------------------------
DefineClockwiseTriangle(int index0,int index1,int index2,int indexStorageBaseOffset)1650 void CHWTessellator::DefineClockwiseTriangle(int index0, int index1, int index2, int indexStorageBaseOffset)
1651 {
1652     // inputs a clockwise triangle, stores a CW or CCW triangle depending on the state
1653     DefineIndex(index0,indexStorageBaseOffset);
1654     bool bWantClockwise = (m_outputPrimitive == PIPE_TESSELLATOR_OUTPUT_TRIANGLE_CW) ? true : false;
1655     if( bWantClockwise )
1656     {
1657         DefineIndex(index1,indexStorageBaseOffset+1);
1658         DefineIndex(index2,indexStorageBaseOffset+2);
1659     }
1660     else
1661     {
1662         DefineIndex(index2,indexStorageBaseOffset+1);
1663         DefineIndex(index1,indexStorageBaseOffset+2);
1664     }
1665 }
1666 
1667 //---------------------------------------------------------------------------------------------------------------------------------
1668 // CHWTessellator::DumpAllPoints()
1669 //---------------------------------------------------------------------------------------------------------------------------------
DumpAllPoints()1670 void CHWTessellator::DumpAllPoints()
1671 {
1672     for( int p = 0; p < m_NumPoints; p++ )
1673     {
1674         DefineIndex(p,m_NumIndices++);
1675     }
1676 }
1677 
1678 //---------------------------------------------------------------------------------------------------------------------------------
1679 // CHWTessellator::DumpAllPointsAsInOrderLineList()
1680 //---------------------------------------------------------------------------------------------------------------------------------
DumpAllPointsAsInOrderLineList()1681 void CHWTessellator::DumpAllPointsAsInOrderLineList()
1682 {
1683     for( int p = 1; p < m_NumPoints; p++ )
1684     {
1685         DefineIndex(p-1,m_NumIndices++);
1686         DefineIndex(p,m_NumIndices++);
1687     }
1688 }
1689 
1690 //---------------------------------------------------------------------------------------------------------------------------------
1691 // RemoveMSB
1692 //---------------------------------------------------------------------------------------------------------------------------------
RemoveMSB(int val)1693 int RemoveMSB(int val)
1694 {
1695     int check;
1696     if( val <= 0x0000ffff ) { check = ( val <= 0x000000ff ) ? 0x00000080 : 0x00008000; }
1697     else                    { check = ( val <= 0x00ffffff ) ? 0x00800000 : 0x80000000; }
1698     for( int i = 0; i < 8; i++, check >>= 1 ) { if( val & check ) return (val & ~check); }
1699     return 0;
1700 }
1701 //---------------------------------------------------------------------------------------------------------------------------------
1702 // GetMSB
1703 //---------------------------------------------------------------------------------------------------------------------------------
GetMSB(int val)1704 int GetMSB(int val)
1705 {
1706     int check;
1707     if( val <= 0x0000ffff ) { check = ( val <= 0x000000ff ) ? 0x00000080 : 0x00008000; }
1708     else                    { check = ( val <= 0x00ffffff ) ? 0x00800000 : 0x80000000; }
1709     for( int i = 0; i < 8; i++, check >>= 1 ) { if( val & check ) return check; }
1710     return 0;
1711 }
1712 
1713 //---------------------------------------------------------------------------------------------------------------------------------
1714 // CHWTessellator::CleanseParameter()
1715 //---------------------------------------------------------------------------------------------------------------------------------
1716 /* NOTHING TO DO FOR FIXED POINT ARITHMETIC!
1717 void CHWTessellator::CleanseParameter(float& parameter)
1718 {
1719     // Clean up [0..1] parameter to guarantee that (1 - (1 - parameter)) == parameter.
1720     parameter = 1.0f - parameter;
1721     parameter = 1.0f - parameter;
1722 
1723 }
1724 */
1725 //---------------------------------------------------------------------------------------------------------------------------------
1726 // CHWTessellator::NumPointsForTessFactor()
1727 //---------------------------------------------------------------------------------------------------------------------------------
NumPointsForTessFactor(FXP fxpTessFactor)1728 int CHWTessellator::NumPointsForTessFactor( FXP fxpTessFactor )
1729 {
1730     int numPoints;
1731     if( Odd() )
1732     {
1733         numPoints = (fxpCeil(FXP_ONE_HALF + (fxpTessFactor+1/*round*/)/2)*2)>>FXP_FRACTION_BITS;
1734     }
1735     else
1736     {
1737         numPoints = ((fxpCeil((fxpTessFactor+1/*round*/)/2)*2)>>FXP_FRACTION_BITS)+1;
1738     }
1739     return numPoints;
1740 }
1741 
1742 //---------------------------------------------------------------------------------------------------------------------------------
1743 // CHWTessellator::ComputeTessFactorContext()
1744 //---------------------------------------------------------------------------------------------------------------------------------
ComputeTessFactorContext(FXP fxpTessFactor,TESS_FACTOR_CONTEXT & TessFactorCtx)1745 void CHWTessellator::ComputeTessFactorContext( FXP fxpTessFactor, TESS_FACTOR_CONTEXT& TessFactorCtx )
1746 {
1747     FXP fxpHalfTessFactor = (fxpTessFactor+1/*round*/)/2;
1748     if( Odd() || (fxpHalfTessFactor == FXP_ONE_HALF)) // fxpHalfTessFactor == 1/2 if TessFactor is 1, but we're pretending we are even.
1749     {
1750         fxpHalfTessFactor += FXP_ONE_HALF;
1751     }
1752     FXP fxpFloorHalfTessFactor = fxpFloor(fxpHalfTessFactor);
1753     FXP fxpCeilHalfTessFactor = fxpCeil(fxpHalfTessFactor);
1754     TessFactorCtx.fxpHalfTessFactorFraction = fxpHalfTessFactor - fxpFloorHalfTessFactor;
1755     //CleanseParameter(TessFactorCtx.fxpHalfTessFactorFraction);
1756     TessFactorCtx.numHalfTessFactorPoints = (fxpCeilHalfTessFactor>>FXP_FRACTION_BITS); // for EVEN, we don't include the point always fixed at the midpoint of the TessFactor
1757     if( fxpCeilHalfTessFactor == fxpFloorHalfTessFactor )
1758     {
1759         TessFactorCtx.splitPointOnFloorHalfTessFactor =  /*pick value to cause this to be ignored*/ TessFactorCtx.numHalfTessFactorPoints+1;
1760     }
1761     else if( Odd() )
1762     {
1763         if( fxpFloorHalfTessFactor == FXP_ONE )
1764         {
1765             TessFactorCtx.splitPointOnFloorHalfTessFactor = 0;
1766         }
1767         else
1768         {
1769 				TessFactorCtx.splitPointOnFloorHalfTessFactor = (RemoveMSB((fxpFloorHalfTessFactor>>FXP_FRACTION_BITS)-1)<<1) + 1;
1770         }
1771     }
1772     else
1773     {
1774 			TessFactorCtx.splitPointOnFloorHalfTessFactor = (RemoveMSB(fxpFloorHalfTessFactor>>FXP_FRACTION_BITS)<<1) + 1;
1775     }
1776     int numFloorSegments = (fxpFloorHalfTessFactor * 2)>>FXP_FRACTION_BITS;
1777     int numCeilSegments = (fxpCeilHalfTessFactor * 2)>>FXP_FRACTION_BITS;
1778     if( Odd() )
1779     {
1780         numFloorSegments -= 1;
1781         numCeilSegments -= 1;
1782     }
1783     TessFactorCtx.fxpInvNumSegmentsOnFloorTessFactor = s_fixedReciprocal[numFloorSegments];
1784     TessFactorCtx.fxpInvNumSegmentsOnCeilTessFactor = s_fixedReciprocal[numCeilSegments];
1785 }
1786 
1787 //---------------------------------------------------------------------------------------------------------------------------------
1788 // CHWTessellator::PlacePointIn1D()
1789 //---------------------------------------------------------------------------------------------------------------------------------
PlacePointIn1D(const TESS_FACTOR_CONTEXT & TessFactorCtx,int point,FXP & fxpLocation)1790 void CHWTessellator::PlacePointIn1D( const TESS_FACTOR_CONTEXT& TessFactorCtx, int point, FXP& fxpLocation )
1791 {
1792     bool bFlip;
1793     if( point >= TessFactorCtx.numHalfTessFactorPoints )
1794     {
1795         point = (TessFactorCtx.numHalfTessFactorPoints << 1) - point;
1796         if( Odd() )
1797         {
1798             point -= 1;
1799         }
1800         bFlip = true;
1801     }
1802     else
1803     {
1804         bFlip = false;
1805     }
1806     if( point == TessFactorCtx.numHalfTessFactorPoints )
1807     {
1808         fxpLocation = FXP_ONE_HALF; // special casing middle since 16 bit fixed math below can't reproduce 0.5 exactly
1809         return;
1810     }
1811     unsigned int indexOnCeilHalfTessFactor = point;
1812     unsigned int indexOnFloorHalfTessFactor = indexOnCeilHalfTessFactor;
1813     if( point > TessFactorCtx.splitPointOnFloorHalfTessFactor )
1814     {
1815         indexOnFloorHalfTessFactor -= 1;
1816     }
1817     // For the fixed point multiplies below, we know the results are <= 16 bits because
1818     // the locations on the halfTessFactor are <= half the number of segments for the total TessFactor.
1819     // So a number divided by a number that is at least twice as big will give
1820     // a result no bigger than 0.5 (which in fixed point is 16 bits in our case)
1821     FXP fxpLocationOnFloorHalfTessFactor = indexOnFloorHalfTessFactor * TessFactorCtx.fxpInvNumSegmentsOnFloorTessFactor;
1822     FXP fxpLocationOnCeilHalfTessFactor = indexOnCeilHalfTessFactor * TessFactorCtx.fxpInvNumSegmentsOnCeilTessFactor;
1823 
1824     // Since we know the numbers calculated above are <= fixed point 0.5, and the equation
1825     // below is just lerping between two values <= fixed point 0.5 (0x00008000), then we know
1826     // that the final result before shifting by 16 bits is no larger than 0x80000000.  Once we
1827     // shift that down by 16, we get the result of lerping 2 numbers <= 0.5, which is obviously
1828     // at most 0.5 (0x00008000)
1829     fxpLocation = fxpLocationOnFloorHalfTessFactor * (FXP_ONE - TessFactorCtx.fxpHalfTessFactorFraction) +
1830                   fxpLocationOnCeilHalfTessFactor * (TessFactorCtx.fxpHalfTessFactorFraction);
1831     fxpLocation = (fxpLocation + FXP_ONE_HALF/*round*/) >> FXP_FRACTION_BITS; // get back to n.16
1832     /* Commenting out floating point version.  Note the parameter cleansing it does is not needed in fixed point.
1833     if( bFlip )
1834         location = 1.0f - location; // complement produces cleansed result.
1835     else
1836         CleanseParameter(location);
1837     */
1838     if( bFlip )
1839     {
1840         fxpLocation = FXP_ONE - fxpLocation;
1841     }
1842 }
1843 
1844 //---------------------------------------------------------------------------------------------------------------------------------
1845 // CHWTessellator::StitchRegular
1846 //---------------------------------------------------------------------------------------------------------------------------------
StitchRegular(bool bTrapezoid,DIAGONALS diagonals,int baseIndexOffset,int numInsideEdgePoints,int insideEdgePointBaseOffset,int outsideEdgePointBaseOffset)1847 void CHWTessellator::StitchRegular(bool bTrapezoid,DIAGONALS diagonals,
1848                                  int baseIndexOffset, int numInsideEdgePoints,
1849                                  int insideEdgePointBaseOffset, int outsideEdgePointBaseOffset)
1850 {
1851     int insidePoint = insideEdgePointBaseOffset;
1852     int outsidePoint = outsideEdgePointBaseOffset;
1853     if( bTrapezoid )
1854     {
1855         DefineClockwiseTriangle(outsidePoint,outsidePoint+1,insidePoint,baseIndexOffset);
1856         baseIndexOffset += 3; outsidePoint++;
1857     }
1858     int p;
1859     switch( diagonals )
1860     {
1861     case DIAGONALS_INSIDE_TO_OUTSIDE:
1862         // Diagonals pointing from inside edge forward towards outside edge
1863         for( p = 0; p < numInsideEdgePoints-1; p++ )
1864         {
1865             DefineClockwiseTriangle(insidePoint,outsidePoint,outsidePoint+1,baseIndexOffset);
1866             baseIndexOffset += 3;
1867 
1868             DefineClockwiseTriangle(insidePoint,outsidePoint+1,insidePoint+1,baseIndexOffset);
1869             baseIndexOffset += 3;
1870             insidePoint++; outsidePoint++;
1871         }
1872         break;
1873     case DIAGONALS_INSIDE_TO_OUTSIDE_EXCEPT_MIDDLE: // Assumes ODD tessellation
1874         // Diagonals pointing from outside edge forward towards inside edge
1875 
1876         // First half
1877         for( p = 0; p < numInsideEdgePoints/2-1; p++ )
1878         {
1879             DefineClockwiseTriangle(outsidePoint,outsidePoint+1,insidePoint,baseIndexOffset);
1880             baseIndexOffset += 3;
1881             DefineClockwiseTriangle(insidePoint,outsidePoint+1,insidePoint+1,baseIndexOffset);
1882             baseIndexOffset += 3;
1883             insidePoint++; outsidePoint++;
1884         }
1885 
1886         // Middle
1887         DefineClockwiseTriangle(outsidePoint,insidePoint+1,insidePoint,baseIndexOffset);
1888         baseIndexOffset += 3;
1889         DefineClockwiseTriangle(outsidePoint,outsidePoint+1,insidePoint+1,baseIndexOffset);
1890         baseIndexOffset += 3;
1891         insidePoint++; outsidePoint++; p+=2;
1892 
1893         // Second half
1894         for( ; p < numInsideEdgePoints; p++ )
1895         {
1896             DefineClockwiseTriangle(outsidePoint,outsidePoint+1,insidePoint,baseIndexOffset);
1897             baseIndexOffset += 3;
1898             DefineClockwiseTriangle(insidePoint,outsidePoint+1,insidePoint+1,baseIndexOffset);
1899             baseIndexOffset += 3;
1900             insidePoint++; outsidePoint++;
1901         }
1902         break;
1903     case DIAGONALS_MIRRORED:
1904         // First half, diagonals pointing from outside of outside edge to inside of inside edge
1905         for( p = 0; p < numInsideEdgePoints/2; p++ )
1906         {
1907             DefineClockwiseTriangle(outsidePoint,insidePoint+1,insidePoint,baseIndexOffset);
1908             baseIndexOffset += 3;
1909             DefineClockwiseTriangle(outsidePoint,outsidePoint+1,insidePoint+1,baseIndexOffset);
1910             baseIndexOffset += 3;
1911             insidePoint++; outsidePoint++;
1912         }
1913         // Second half, diagonals pointing from inside of inside edge to outside of outside edge
1914         for( ; p < numInsideEdgePoints-1; p++ )
1915         {
1916             DefineClockwiseTriangle(insidePoint,outsidePoint,outsidePoint+1,baseIndexOffset);
1917             baseIndexOffset += 3;
1918             DefineClockwiseTriangle(insidePoint,outsidePoint+1,insidePoint+1,baseIndexOffset);
1919             baseIndexOffset += 3;
1920             insidePoint++; outsidePoint++;
1921         }
1922         break;
1923     }
1924     if( bTrapezoid )
1925     {
1926         DefineClockwiseTriangle(outsidePoint,outsidePoint+1,insidePoint,baseIndexOffset);
1927         baseIndexOffset += 3;
1928     }
1929 }
1930 
1931 //---------------------------------------------------------------------------------------------------------------------------------
1932 // CHWTessellator::StitchTransition()
1933 //---------------------------------------------------------------------------------------------------------------------------------
StitchTransition(int baseIndexOffset,int insideEdgePointBaseOffset,int insideNumHalfTessFactorPoints,TESSELLATOR_PARITY insideEdgeTessFactorParity,int outsideEdgePointBaseOffset,int outsideNumHalfTessFactorPoints,TESSELLATOR_PARITY outsideTessFactorParity)1934 void CHWTessellator::StitchTransition(int baseIndexOffset,
1935                                     int insideEdgePointBaseOffset, int insideNumHalfTessFactorPoints,
1936                                     TESSELLATOR_PARITY insideEdgeTessFactorParity,
1937                                     int outsideEdgePointBaseOffset, int outsideNumHalfTessFactorPoints,
1938                                     TESSELLATOR_PARITY outsideTessFactorParity
1939 )
1940 {
1941     // Tables to assist in the stitching of 2 rows of points having arbitrary TessFactors.
1942     // The stitching order is governed by Ruler Function vertex split ordering (see external documentation).
1943     //
1944     // The contents of the finalPointPositionTable are where vertex i [0..33] ends up on the half-edge
1945     // at the max tessellation amount given ruler-function split order.
1946     // Recall the other half of an edge is mirrored, so we only need to deal with one half.
1947     // This table is used to decide when to advance a point on the interior or exterior.
1948     // It supports odd TessFactor up to 65 and even TessFactor up to 64.
1949     static const int finalPointPositionTable[33] =
1950             { 0, 32, 16, 8, 17, 4, 18, 9, 19, 2, 20, 10, 21, 5, 22, 11, 23,
1951               1, 24, 12, 25, 6, 26, 13, 27, 3, 28, 14, 29, 7, 30, 15, 31 };
1952 
1953     // The loopStart and loopEnd tables below just provide optimal loop bounds for the
1954     // stitching algorithm further below, for any given halfTssFactor.
1955     // There is probably a better way to encode this...
1956 
1957     // loopStart[halfTessFactor] encodes the FIRST entry in finalPointPositionTable[] above which is
1958     // less than halfTessFactor.  Exceptions are entry 0 and 1, which are set up to skip the loop.
1959     static const int loopStart[33] =
1960             {1,1,17,9,9,5,5,5,5,3,3,3,3,3,3,3,3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2};
1961     // loopStart[halfTessFactor] encodes the LAST entry in finalPointPositionTable[] above which is
1962     // less than halfTessFactor.  Exceptions are entry 0 and 1, which are set up to skip the loop.
1963     static const int loopEnd[33] =
1964             {0,0,17,17,25,25,25,25,29,29,29,29,29,29,29,29,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,32};
1965 
1966     if( TESSELLATOR_PARITY_ODD == insideEdgeTessFactorParity )
1967     {
1968         insideNumHalfTessFactorPoints -= 1;
1969     }
1970     if( TESSELLATOR_PARITY_ODD == outsideTessFactorParity )
1971     {
1972         outsideNumHalfTessFactorPoints -= 1;
1973     }
1974     // Walk first half
1975     int outsidePoint = outsideEdgePointBaseOffset;
1976     int insidePoint = insideEdgePointBaseOffset;
1977 
1978     // iStart,iEnd are a small optimization so the loop below doesn't have to go from 0 up to 31
1979     int iStart = min(loopStart[insideNumHalfTessFactorPoints],loopStart[outsideNumHalfTessFactorPoints]);
1980     int iEnd = max(loopEnd[insideNumHalfTessFactorPoints],loopEnd[outsideNumHalfTessFactorPoints]);
1981 
1982     if( finalPointPositionTable[0] < outsideNumHalfTessFactorPoints ) // since we dont' start the loop at 0 below, we need a special case.
1983     {
1984         // Advance outside
1985         DefineClockwiseTriangle(outsidePoint,outsidePoint+1,insidePoint,baseIndexOffset);
1986         baseIndexOffset += 3; outsidePoint++;
1987     }
1988 
1989     for(int i = iStart; i <= iEnd; i++)
1990     {
1991         if( /*(i>0) && <-- not needed since iStart is never 0*/(finalPointPositionTable[i] < insideNumHalfTessFactorPoints))
1992         {
1993             // Advance inside
1994             DefineClockwiseTriangle(insidePoint,outsidePoint,insidePoint+1,baseIndexOffset);
1995             baseIndexOffset += 3; insidePoint++;
1996         }
1997         if((finalPointPositionTable[i] < outsideNumHalfTessFactorPoints))
1998         {
1999             // Advance outside
2000             DefineClockwiseTriangle(outsidePoint,outsidePoint+1,insidePoint,baseIndexOffset);
2001             baseIndexOffset += 3; outsidePoint++;
2002         }
2003     }
2004 
2005     if( (insideEdgeTessFactorParity != outsideTessFactorParity) || (insideEdgeTessFactorParity == TESSELLATOR_PARITY_ODD))
2006     {
2007         if( insideEdgeTessFactorParity == outsideTessFactorParity )
2008         {
2009             // Quad in the middle
2010             DefineClockwiseTriangle(insidePoint,outsidePoint,insidePoint+1,baseIndexOffset);
2011             baseIndexOffset += 3;
2012             DefineClockwiseTriangle(insidePoint+1,outsidePoint,outsidePoint+1,baseIndexOffset);
2013             baseIndexOffset += 3;
2014             insidePoint++;
2015             outsidePoint++;
2016         }
2017         else if( TESSELLATOR_PARITY_EVEN == insideEdgeTessFactorParity )
2018         {
2019             // Triangle pointing inside
2020             DefineClockwiseTriangle(insidePoint,outsidePoint,outsidePoint+1,baseIndexOffset);
2021             baseIndexOffset += 3;
2022             outsidePoint++;
2023         }
2024         else
2025         {
2026             // Triangle pointing outside
2027             DefineClockwiseTriangle(insidePoint,outsidePoint,insidePoint+1,baseIndexOffset);
2028             baseIndexOffset += 3;
2029             insidePoint++;
2030         }
2031     }
2032 
2033     // Walk second half.
2034     for(int i = iEnd; i >= iStart; i--)
2035     {
2036         if((finalPointPositionTable[i] < outsideNumHalfTessFactorPoints))
2037         {
2038             // Advance outside
2039             DefineClockwiseTriangle(outsidePoint,outsidePoint+1,insidePoint,baseIndexOffset);
2040             baseIndexOffset += 3; outsidePoint++;
2041         }
2042         if( /*(i>0) && <-- not needed since iStart is never 0*/ (finalPointPositionTable[i] < insideNumHalfTessFactorPoints))
2043         {
2044             // Advance inside
2045             DefineClockwiseTriangle(insidePoint,outsidePoint,insidePoint+1,baseIndexOffset);
2046             baseIndexOffset += 3; insidePoint++;
2047         }
2048     }
2049     // Below case is not needed if we didn't optimize loop above and made it run from 31 down to 0.
2050     if((finalPointPositionTable[0] < outsideNumHalfTessFactorPoints))
2051     {
2052         DefineClockwiseTriangle(outsidePoint,outsidePoint+1,insidePoint,baseIndexOffset);
2053         baseIndexOffset += 3; outsidePoint++;
2054     }
2055 }
2056 
2057 //---------------------------------------------------------------------------------------------------------------------------------
2058 // CHWTessellator::PatchIndexValue()
2059 //--------------------------------------------------------------------------------------------------------------------------------
PatchIndexValue(int index)2060 int CHWTessellator::PatchIndexValue(int index)
2061 {
2062     if( m_bUsingPatchedIndices )
2063     {
2064         if( index >= m_IndexPatchContext.outsidePointIndexPatchBase ) // assumed remapped outide indices are > remapped inside vertices
2065         {
2066             if( index == m_IndexPatchContext.outsidePointIndexBadValue )
2067                 index = m_IndexPatchContext.outsidePointIndexReplacementValue;
2068             else
2069                 index += m_IndexPatchContext.outsidePointIndexDeltaToRealValue;
2070         }
2071         else
2072         {
2073             if( index == m_IndexPatchContext.insidePointIndexBadValue )
2074                 index = m_IndexPatchContext.insidePointIndexReplacementValue;
2075             else
2076                 index += m_IndexPatchContext.insidePointIndexDeltaToRealValue;
2077         }
2078     }
2079     else if( m_bUsingPatchedIndices2 )
2080     {
2081         if( index >= m_IndexPatchContext2.baseIndexToInvert )
2082         {
2083             if( index == m_IndexPatchContext2.cornerCaseBadValue )
2084             {
2085                 index = m_IndexPatchContext2.cornerCaseReplacementValue;
2086             }
2087             else
2088             {
2089                 index = m_IndexPatchContext2.indexInversionEndPoint - index;
2090             }
2091         }
2092         else if( index == m_IndexPatchContext2.cornerCaseBadValue )
2093         {
2094             index = m_IndexPatchContext2.cornerCaseReplacementValue;
2095         }
2096     }
2097     return index;
2098 }
2099 
2100 
2101 //=================================================================================================================================
2102 // CHLSLTessellator
2103 //=================================================================================================================================
2104 
2105 //---------------------------------------------------------------------------------------------------------------------------------
2106 // CHLSLTessellator::CHLSLTessellator
2107 //---------------------------------------------------------------------------------------------------------------------------------
CHLSLTessellator()2108 CHLSLTessellator::CHLSLTessellator()
2109 {
2110     m_LastComputedTessFactors[0] = m_LastComputedTessFactors[1] = m_LastComputedTessFactors[2] =
2111     m_LastComputedTessFactors[3] = m_LastComputedTessFactors[4] = m_LastComputedTessFactors[5] = 0;
2112 }
2113 
2114 //---------------------------------------------------------------------------------------------------------------------------------
2115 // CHLSLTessellator::Init
2116 // User calls this.
2117 //---------------------------------------------------------------------------------------------------------------------------------
Init(PIPE_TESSELLATOR_PARTITIONING partitioning,PIPE_TESSELLATOR_REDUCTION insideTessFactorReduction,PIPE_TESSELLATOR_QUAD_REDUCTION_AXIS quadInsideTessFactorReductionAxis,PIPE_TESSELLATOR_OUTPUT_PRIMITIVE outputPrimitive)2118 void CHLSLTessellator::Init(
2119     PIPE_TESSELLATOR_PARTITIONING       partitioning,
2120     PIPE_TESSELLATOR_REDUCTION          insideTessFactorReduction,
2121     PIPE_TESSELLATOR_QUAD_REDUCTION_AXIS quadInsideTessFactorReductionAxis,
2122     PIPE_TESSELLATOR_OUTPUT_PRIMITIVE   outputPrimitive)
2123 {
2124     CHWTessellator::Init(partitioning,outputPrimitive);
2125     m_LastComputedTessFactors[0] = m_LastComputedTessFactors[1] = m_LastComputedTessFactors[2] =
2126     m_LastComputedTessFactors[3] = m_LastComputedTessFactors[4] = m_LastComputedTessFactors[5] = 0;
2127     m_partitioning = partitioning;
2128     m_originalPartitioning = partitioning;
2129     switch( partitioning )
2130     {
2131     case PIPE_TESSELLATOR_PARTITIONING_INTEGER:
2132     default:
2133         break;
2134     case PIPE_TESSELLATOR_PARTITIONING_FRACTIONAL_ODD:
2135         m_parity = TESSELLATOR_PARITY_ODD;
2136         break;
2137     case PIPE_TESSELLATOR_PARTITIONING_FRACTIONAL_EVEN:
2138         m_parity = TESSELLATOR_PARITY_EVEN;
2139         break;
2140     }
2141     m_originalParity = m_parity;
2142     m_outputPrimitive = outputPrimitive;
2143     m_insideTessFactorReduction = insideTessFactorReduction;
2144     m_quadInsideTessFactorReductionAxis = quadInsideTessFactorReductionAxis;
2145 }
2146 //---------------------------------------------------------------------------------------------------------------------------------
2147 // CHLSLTessellator::TessellateQuadDomain
2148 // User calls this
2149 //---------------------------------------------------------------------------------------------------------------------------------
TessellateQuadDomain(float tessFactor_Ueq0,float tessFactor_Veq0,float tessFactor_Ueq1,float tessFactor_Veq1,float insideTessFactorScaleU,float insideTessFactorScaleV)2150 void CHLSLTessellator::TessellateQuadDomain( float tessFactor_Ueq0, float tessFactor_Veq0, float tessFactor_Ueq1, float tessFactor_Veq1,
2151                                          float insideTessFactorScaleU, float insideTessFactorScaleV )
2152 {
2153     QuadHLSLProcessTessFactors(tessFactor_Ueq0,tessFactor_Veq0,tessFactor_Ueq1,tessFactor_Veq1,insideTessFactorScaleU,insideTessFactorScaleV);
2154 
2155     CHWTessellator::TessellateQuadDomain(m_LastComputedTessFactors[0],m_LastComputedTessFactors[1],m_LastComputedTessFactors[2],m_LastComputedTessFactors[3],
2156                                          m_LastComputedTessFactors[4],m_LastComputedTessFactors[5]);
2157 }
2158 
2159 //---------------------------------------------------------------------------------------------------------------------------------
2160 // CHLSLTessellator::QuadHLSLProcessTessFactors
2161 //---------------------------------------------------------------------------------------------------------------------------------
QuadHLSLProcessTessFactors(float tessFactor_Ueq0,float tessFactor_Veq0,float tessFactor_Ueq1,float tessFactor_Veq1,float insideTessFactorScaleU,float insideTessFactorScaleV)2162 void CHLSLTessellator::QuadHLSLProcessTessFactors( float tessFactor_Ueq0, float tessFactor_Veq0, float tessFactor_Ueq1, float tessFactor_Veq1,
2163                                                float insideTessFactorScaleU, float insideTessFactorScaleV )
2164 {
2165     if( !(tessFactor_Ueq0 > 0) ||// NaN will pass
2166         !(tessFactor_Veq0 > 0) ||
2167         !(tessFactor_Ueq1 > 0) ||
2168         !(tessFactor_Veq1 > 0) )
2169     {
2170         m_LastUnRoundedComputedTessFactors[0] = tessFactor_Ueq0;
2171         m_LastUnRoundedComputedTessFactors[1] = tessFactor_Veq0;
2172         m_LastUnRoundedComputedTessFactors[2] = tessFactor_Ueq1;
2173         m_LastUnRoundedComputedTessFactors[3] = tessFactor_Veq1;
2174         m_LastUnRoundedComputedTessFactors[4] = 0;
2175         m_LastUnRoundedComputedTessFactors[5] = 0;
2176         m_LastComputedTessFactors[0] =
2177         m_LastComputedTessFactors[1] =
2178         m_LastComputedTessFactors[2] =
2179         m_LastComputedTessFactors[3] =
2180         m_LastComputedTessFactors[4] =
2181         m_LastComputedTessFactors[5] = 0;
2182         return;
2183     }
2184 
2185     CleanupFloatTessFactor(tessFactor_Ueq0);// clamp to [1.0f..INF], NaN->1.0f
2186     CleanupFloatTessFactor(tessFactor_Veq0);
2187     CleanupFloatTessFactor(tessFactor_Ueq1);
2188     CleanupFloatTessFactor(tessFactor_Veq1);
2189 
2190     // Save off tessFactors so they can be returned to app
2191     m_LastUnRoundedComputedTessFactors[0] = tessFactor_Ueq0;
2192     m_LastUnRoundedComputedTessFactors[1] = tessFactor_Veq0;
2193     m_LastUnRoundedComputedTessFactors[2] = tessFactor_Ueq1;
2194     m_LastUnRoundedComputedTessFactors[3] = tessFactor_Veq1;
2195 
2196     // Process outside tessFactors
2197     float outsideTessFactor[QUAD_EDGES] = {tessFactor_Ueq0, tessFactor_Veq0, tessFactor_Ueq1, tessFactor_Veq1};
2198     int edge, axis;
2199     TESSELLATOR_PARITY insideTessFactorParity[QUAD_AXES];
2200     if( Pow2Partitioning() || IntegerPartitioning() )
2201     {
2202         for( edge = 0; edge < QUAD_EDGES; edge++ )
2203         {
2204             RoundUpTessFactor(outsideTessFactor[edge]);
2205             ClampTessFactor(outsideTessFactor[edge]); // clamp unbounded user input based on tessellation mode
2206         }
2207     }
2208     else
2209     {
2210         SetTessellationParity(m_originalParity); // ClampTessFactor needs it
2211         for( edge = 0; edge < QUAD_EDGES; edge++ )
2212         {
2213             ClampTessFactor(outsideTessFactor[edge]); // clamp unbounded user input based on tessellation mode
2214         }
2215     }
2216 
2217     // Compute inside TessFactors
2218     float insideTessFactor[QUAD_AXES];
2219     if( m_quadInsideTessFactorReductionAxis == PIPE_TESSELLATOR_QUAD_REDUCTION_1_AXIS )
2220     {
2221         switch( m_insideTessFactorReduction )
2222         {
2223         case PIPE_TESSELLATOR_REDUCTION_MIN:
2224             insideTessFactor[U] = tess_fmin(tess_fmin(tessFactor_Veq0,tessFactor_Veq1),tess_fmin(tessFactor_Ueq0,tessFactor_Ueq1));
2225             break;
2226         case PIPE_TESSELLATOR_REDUCTION_MAX:
2227             insideTessFactor[U] = tess_fmax(tess_fmax(tessFactor_Veq0,tessFactor_Veq1),tess_fmax(tessFactor_Ueq0,tessFactor_Ueq1));
2228             break;
2229         case PIPE_TESSELLATOR_REDUCTION_AVERAGE:
2230             insideTessFactor[U] = (tessFactor_Veq0 + tessFactor_Veq1 + tessFactor_Ueq0 + tessFactor_Ueq1) / 4;
2231             break;
2232         default:
2233             unreachable("impossible m_insideTessFactorReduction");
2234         }
2235         // Scale inside tessFactor based on user scale factor.
2236 
2237         ClampFloatTessFactorScale(insideTessFactorScaleU); // clamp scale value to [0..1], NaN->0
2238         insideTessFactor[U] = insideTessFactor[U]*insideTessFactorScaleU;
2239 
2240         // Compute inside parity
2241         if( Pow2Partitioning() || IntegerPartitioning() )
2242         {
2243             ClampTessFactor(insideTessFactor[U]); // clamp reduction + scale result that is based on unbounded user input
2244             m_LastUnRoundedComputedTessFactors[4] = m_LastUnRoundedComputedTessFactors[5] = insideTessFactor[U]; // Save off TessFactors so they can be returned to app
2245             RoundUpTessFactor(insideTessFactor[U]);
2246             insideTessFactorParity[U] =
2247             insideTessFactorParity[V] =
2248                 (isEven(insideTessFactor[U]) || (FLOAT_ONE == insideTessFactor[U]) )
2249                 ? TESSELLATOR_PARITY_EVEN : TESSELLATOR_PARITY_ODD;
2250         }
2251         else
2252         {
2253             ClampTessFactor(insideTessFactor[U]); // clamp reduction + scale result that is based on unbounded user input
2254             m_LastUnRoundedComputedTessFactors[4] = m_LastUnRoundedComputedTessFactors[5] = insideTessFactor[U]; // Save off TessFactors so they can be returned to app
2255             // no parity changes for fractional tessellation - just use what the user requested
2256             insideTessFactorParity[U] = insideTessFactorParity[V] = m_originalParity;
2257         }
2258 
2259         // To prevent snapping on edges, the "picture frame" comes
2260         // in using avg or max (and ignore inside TessFactor scaling) until it is at least 3.
2261         if( (TESSELLATOR_PARITY_ODD == insideTessFactorParity[U]) &&
2262             (insideTessFactor[U] < FLOAT_THREE) )
2263         {
2264             if(PIPE_TESSELLATOR_REDUCTION_MAX == m_insideTessFactorReduction)
2265             {
2266                 insideTessFactor[U] = tess_fmin(FLOAT_THREE,tess_fmax(tess_fmax(tessFactor_Veq0,tessFactor_Veq1),tess_fmax(tessFactor_Ueq0,tessFactor_Ueq1)));
2267             }
2268             else
2269             {
2270                 insideTessFactor[U] = tess_fmin(FLOAT_THREE,(tessFactor_Veq0 + tessFactor_Veq1 + tessFactor_Ueq0 + tessFactor_Ueq1) / 4);
2271             }
2272             ClampTessFactor(insideTessFactor[U]); // clamp reduction result that is based on unbounded user input
2273             m_LastUnRoundedComputedTessFactors[4] = m_LastUnRoundedComputedTessFactors[5] = insideTessFactor[U]; // Save off TessFactors so they can be returned to app
2274             if( IntegerPartitioning())
2275             {
2276                 RoundUpTessFactor(insideTessFactor[U]);
2277                 insideTessFactorParity[U] =
2278                 insideTessFactorParity[V] = isEven(insideTessFactor[U]) ? TESSELLATOR_PARITY_EVEN : TESSELLATOR_PARITY_ODD;
2279             }
2280         }
2281         insideTessFactor[V] = insideTessFactor[U];
2282     }
2283     else
2284     {
2285         switch( m_insideTessFactorReduction )
2286         {
2287         case PIPE_TESSELLATOR_REDUCTION_MIN:
2288             insideTessFactor[U] = tess_fmin(tessFactor_Veq0,tessFactor_Veq1);
2289             insideTessFactor[V] = tess_fmin(tessFactor_Ueq0,tessFactor_Ueq1);
2290             break;
2291         case PIPE_TESSELLATOR_REDUCTION_MAX:
2292             insideTessFactor[U] = tess_fmax(tessFactor_Veq0,tessFactor_Veq1);
2293             insideTessFactor[V] = tess_fmax(tessFactor_Ueq0,tessFactor_Ueq1);
2294             break;
2295         case PIPE_TESSELLATOR_REDUCTION_AVERAGE:
2296             insideTessFactor[U] = (tessFactor_Veq0 + tessFactor_Veq1) / 2;
2297             insideTessFactor[V] = (tessFactor_Ueq0 + tessFactor_Ueq1) / 2;
2298             break;
2299         default:
2300             unreachable("impossible m_insideTessFactorReduction");
2301         }
2302         // Scale inside tessFactors based on user scale factor.
2303 
2304         ClampFloatTessFactorScale(insideTessFactorScaleU); // clamp scale value to [0..1], NaN->0
2305         ClampFloatTessFactorScale(insideTessFactorScaleV);
2306         insideTessFactor[U] = insideTessFactor[U]*insideTessFactorScaleU;
2307         insideTessFactor[V] = insideTessFactor[V]*insideTessFactorScaleV;
2308 
2309         // Compute inside parity
2310         if( Pow2Partitioning() || IntegerPartitioning() )
2311         {
2312             for( axis = 0; axis < QUAD_AXES; axis++ )
2313             {
2314                 ClampTessFactor(insideTessFactor[axis]); // clamp reduction + scale result that is based on unbounded user input
2315                 m_LastUnRoundedComputedTessFactors[4+axis] = insideTessFactor[axis]; // Save off TessFactors so they can be returned to app
2316                 RoundUpTessFactor(insideTessFactor[axis]);
2317                 insideTessFactorParity[axis] =
2318                     (isEven(insideTessFactor[axis]) || (FLOAT_ONE == insideTessFactor[axis]) )
2319                     ? TESSELLATOR_PARITY_EVEN : TESSELLATOR_PARITY_ODD;
2320             }
2321         }
2322         else
2323         {
2324             ClampTessFactor(insideTessFactor[U]); // clamp reduction + scale result that is based on unbounded user input
2325             ClampTessFactor(insideTessFactor[V]); // clamp reduction + scale result that is based on unbounded user input
2326             m_LastUnRoundedComputedTessFactors[4] = insideTessFactor[U]; // Save off TessFactors so they can be returned to app
2327             m_LastUnRoundedComputedTessFactors[5] = insideTessFactor[V]; // Save off TessFactors so they can be returned to app
2328              // no parity changes for fractional tessellation - just use what the user requested
2329             insideTessFactorParity[U] = insideTessFactorParity[V] = m_originalParity;
2330         }
2331 
2332         // To prevent snapping on edges, the "picture frame" comes
2333         // in using avg or max (and ignore inside TessFactor scaling) until it is at least 3.
2334         if( (TESSELLATOR_PARITY_ODD == insideTessFactorParity[U]) &&
2335             (insideTessFactor[U] < FLOAT_THREE) )
2336         {
2337             if(PIPE_TESSELLATOR_REDUCTION_MAX == m_insideTessFactorReduction)
2338             {
2339                 insideTessFactor[U] = tess_fmin(FLOAT_THREE,tess_fmax(tessFactor_Veq0,tessFactor_Veq1));
2340             }
2341             else
2342             {
2343                 insideTessFactor[U] = tess_fmin(FLOAT_THREE,(tessFactor_Veq0 + tessFactor_Veq1) / 2);
2344             }
2345             ClampTessFactor(insideTessFactor[U]); // clamp reduction result that is based on unbounded user input
2346             m_LastUnRoundedComputedTessFactors[4] = insideTessFactor[U]; // Save off TessFactors so they can be returned to app
2347             if( IntegerPartitioning())
2348             {
2349                 RoundUpTessFactor(insideTessFactor[U]);
2350                 insideTessFactorParity[U] = isEven(insideTessFactor[U]) ? TESSELLATOR_PARITY_EVEN : TESSELLATOR_PARITY_ODD;
2351             }
2352         }
2353 
2354         if( (TESSELLATOR_PARITY_ODD == insideTessFactorParity[V]) &&
2355             (insideTessFactor[V] < FLOAT_THREE) )
2356         {
2357             if(PIPE_TESSELLATOR_REDUCTION_MAX == m_insideTessFactorReduction)
2358             {
2359                 insideTessFactor[V] = tess_fmin(FLOAT_THREE,tess_fmax(tessFactor_Ueq0,tessFactor_Ueq1));
2360             }
2361             else
2362             {
2363                 insideTessFactor[V] = tess_fmin(FLOAT_THREE,(tessFactor_Ueq0 + tessFactor_Ueq1) / 2);
2364             }
2365             ClampTessFactor(insideTessFactor[V]);// clamp reduction result that is based on unbounded user input
2366             m_LastUnRoundedComputedTessFactors[5] = insideTessFactor[V]; // Save off TessFactors so they can be returned to app
2367             if( IntegerPartitioning())
2368             {
2369                 RoundUpTessFactor(insideTessFactor[V]);
2370                 insideTessFactorParity[V] = isEven(insideTessFactor[V]) ? TESSELLATOR_PARITY_EVEN : TESSELLATOR_PARITY_ODD;
2371             }
2372         }
2373 
2374         for( axis = 0; axis < QUAD_AXES; axis++ )
2375         {
2376             if( TESSELLATOR_PARITY_ODD == insideTessFactorParity[axis] )
2377             {
2378                 // Ensure the first ring ("picture frame") interpolates in on all sides
2379                 // as much as the side with the minimum TessFactor.  Prevents snapping to edge.
2380                 if( (insideTessFactor[axis] < FLOAT_THREE) && (insideTessFactor[axis] < insideTessFactor[(axis+1)&0x1]))
2381                 {
2382                     insideTessFactor[axis] = tess_fmin(insideTessFactor[(axis+1)&0x1],FLOAT_THREE);
2383                     m_LastUnRoundedComputedTessFactors[4+axis] = insideTessFactor[axis]; // Save off TessFactors so they can be returned to app
2384                 }
2385             }
2386         }
2387     }
2388 
2389     // Save off TessFactors so they can be returned to app
2390     m_LastComputedTessFactors[0] = outsideTessFactor[Ueq0];
2391     m_LastComputedTessFactors[1] = outsideTessFactor[Veq0];
2392     m_LastComputedTessFactors[2] = outsideTessFactor[Ueq1];
2393     m_LastComputedTessFactors[3] = outsideTessFactor[Veq1];
2394     m_LastComputedTessFactors[4] = insideTessFactor[U];
2395     m_LastComputedTessFactors[5] = insideTessFactor[V];
2396 }
2397 
2398 //---------------------------------------------------------------------------------------------------------------------------------
2399 // CHLSLTessellator::TessellateTriDomain
2400 // User calls this
2401 //---------------------------------------------------------------------------------------------------------------------------------
TessellateTriDomain(float tessFactor_Ueq0,float tessFactor_Veq0,float tessFactor_Weq0,float insideTessFactorScale)2402 void CHLSLTessellator::TessellateTriDomain( float tessFactor_Ueq0, float tessFactor_Veq0, float tessFactor_Weq0,
2403                                         float insideTessFactorScale )
2404 {
2405     TriHLSLProcessTessFactors(tessFactor_Ueq0,tessFactor_Veq0,tessFactor_Weq0,insideTessFactorScale);
2406 
2407     CHWTessellator::TessellateTriDomain(m_LastComputedTessFactors[0],m_LastComputedTessFactors[1],m_LastComputedTessFactors[2],m_LastComputedTessFactors[3]);
2408 }
2409 
2410 //---------------------------------------------------------------------------------------------------------------------------------
2411 // CHLSLTessellator::TriHLSLProcessTessFactors
2412 //---------------------------------------------------------------------------------------------------------------------------------
TriHLSLProcessTessFactors(float tessFactor_Ueq0,float tessFactor_Veq0,float tessFactor_Weq0,float insideTessFactorScale)2413 void CHLSLTessellator::TriHLSLProcessTessFactors( float tessFactor_Ueq0, float tessFactor_Veq0, float tessFactor_Weq0,
2414                                   float insideTessFactorScale )
2415 {
2416     if( !(tessFactor_Ueq0 > 0) || // NaN will pass
2417         !(tessFactor_Veq0 > 0) ||
2418         !(tessFactor_Weq0 > 0) )
2419     {
2420         m_LastUnRoundedComputedTessFactors[0] = tessFactor_Ueq0;
2421         m_LastUnRoundedComputedTessFactors[1] = tessFactor_Veq0;
2422         m_LastUnRoundedComputedTessFactors[2] = tessFactor_Weq0;
2423         m_LastUnRoundedComputedTessFactors[3] =
2424         m_LastComputedTessFactors[0] =
2425         m_LastComputedTessFactors[1] =
2426         m_LastComputedTessFactors[2] =
2427         m_LastComputedTessFactors[3] = 0;
2428         return;
2429     }
2430 
2431     CleanupFloatTessFactor(tessFactor_Ueq0); // clamp to [1.0f..INF], NaN->1.0f
2432     CleanupFloatTessFactor(tessFactor_Veq0);
2433     CleanupFloatTessFactor(tessFactor_Weq0);
2434 
2435     // Save off TessFactors so they can be returned to app
2436     m_LastUnRoundedComputedTessFactors[0] = tessFactor_Ueq0;
2437     m_LastUnRoundedComputedTessFactors[1] = tessFactor_Veq0;
2438     m_LastUnRoundedComputedTessFactors[2] = tessFactor_Weq0;
2439 
2440     // Process outside TessFactors
2441     float outsideTessFactor[TRI_EDGES] = {tessFactor_Ueq0, tessFactor_Veq0, tessFactor_Weq0};
2442     int edge;
2443     if( Pow2Partitioning() || IntegerPartitioning() )
2444     {
2445         for( edge = 0; edge < TRI_EDGES; edge++ )
2446         {
2447             RoundUpTessFactor(outsideTessFactor[edge]); // for pow2 this rounds to pow2
2448             ClampTessFactor(outsideTessFactor[edge]); // clamp unbounded user input based on tessellation mode
2449         }
2450     }
2451     else
2452     {
2453         for( edge = 0; edge < TRI_EDGES; edge++ )
2454         {
2455             ClampTessFactor(outsideTessFactor[edge]); // clamp unbounded user input based on tessellation mode
2456         }
2457     }
2458 
2459     // Compute inside TessFactor
2460     float insideTessFactor;
2461     switch( m_insideTessFactorReduction )
2462     {
2463     case PIPE_TESSELLATOR_REDUCTION_MIN:
2464         insideTessFactor = tess_fmin(tess_fmin(tessFactor_Ueq0,tessFactor_Veq0),tessFactor_Weq0);
2465         break;
2466     case PIPE_TESSELLATOR_REDUCTION_MAX:
2467         insideTessFactor = tess_fmax(tess_fmax(tessFactor_Ueq0,tessFactor_Veq0),tessFactor_Weq0);
2468         break;
2469     case PIPE_TESSELLATOR_REDUCTION_AVERAGE:
2470         insideTessFactor = (tessFactor_Ueq0 + tessFactor_Veq0 + tessFactor_Weq0) / 3;
2471         break;
2472     default:
2473         unreachable("impossible m_insideTessFactorReduction");
2474     }
2475 
2476     // Scale inside TessFactor based on user scale factor.
2477     ClampFloatTessFactorScale(insideTessFactorScale); // clamp scale value to [0..1], NaN->0
2478     insideTessFactor = insideTessFactor*tess_fmin(FLOAT_ONE,insideTessFactorScale);
2479 
2480     ClampTessFactor(insideTessFactor); // clamp reduction + scale result that is based on unbounded user input
2481     m_LastUnRoundedComputedTessFactors[3] = insideTessFactor;// Save off TessFactors so they can be returned to app
2482     TESSELLATOR_PARITY parity;
2483     if( Pow2Partitioning() || IntegerPartitioning() )
2484     {
2485         RoundUpTessFactor(insideTessFactor);
2486         parity = (isEven(insideTessFactor) || (FLOAT_ONE == insideTessFactor))
2487                                         ? TESSELLATOR_PARITY_EVEN : TESSELLATOR_PARITY_ODD;
2488     }
2489     else
2490     {
2491         parity = m_originalParity;
2492     }
2493 
2494     if( (TESSELLATOR_PARITY_ODD == parity) &&
2495         (insideTessFactor < FLOAT_THREE))
2496     {
2497         // To prevent snapping on edges, the "picture frame" comes
2498         // in using avg or max (and ignore inside TessFactor scaling) until it is at least 3.
2499         if(PIPE_TESSELLATOR_REDUCTION_MAX == m_insideTessFactorReduction)
2500         {
2501             insideTessFactor = tess_fmin(FLOAT_THREE,tess_fmax(tessFactor_Ueq0,tess_fmax(tessFactor_Veq0,tessFactor_Weq0)));
2502         }
2503         else
2504         {
2505             insideTessFactor = tess_fmin(FLOAT_THREE,(tessFactor_Ueq0 + tessFactor_Veq0 + tessFactor_Weq0) / 3);
2506         }
2507         ClampTessFactor(insideTessFactor); // clamp reduction result that is based on unbounded user input
2508         m_LastUnRoundedComputedTessFactors[3] = insideTessFactor;// Save off TessFactors so they can be returned to app
2509         if( IntegerPartitioning())
2510         {
2511             RoundUpTessFactor(insideTessFactor);
2512         }
2513     }
2514 
2515     // Save off TessFactors so they can be returned to app
2516     m_LastComputedTessFactors[0] = outsideTessFactor[Ueq0];
2517     m_LastComputedTessFactors[1] = outsideTessFactor[Veq0];
2518     m_LastComputedTessFactors[2] = outsideTessFactor[Weq0];
2519     m_LastComputedTessFactors[3] = insideTessFactor;
2520 }
2521 
2522 //---------------------------------------------------------------------------------------------------------------------------------
2523 // CHLSLTessellator::TessellateIsoLineDomain
2524 // User calls this.
2525 //---------------------------------------------------------------------------------------------------------------------------------
TessellateIsoLineDomain(float TessFactor_U_LineDetail,float TessFactor_V_LineDensity)2526 void CHLSLTessellator::TessellateIsoLineDomain( float TessFactor_U_LineDetail, float TessFactor_V_LineDensity )
2527 {
2528     IsoLineHLSLProcessTessFactors(TessFactor_V_LineDensity,TessFactor_U_LineDetail);
2529     CHWTessellator::TessellateIsoLineDomain(m_LastComputedTessFactors[0],m_LastComputedTessFactors[1]);
2530 }
2531 
2532 //---------------------------------------------------------------------------------------------------------------------------------
2533 // CHLSLTessellator::IsoLineHLSLProcessTessFactors
2534 //---------------------------------------------------------------------------------------------------------------------------------
IsoLineHLSLProcessTessFactors(float TessFactor_V_LineDensity,float TessFactor_U_LineDetail)2535 void CHLSLTessellator::IsoLineHLSLProcessTessFactors( float TessFactor_V_LineDensity, float TessFactor_U_LineDetail )
2536 {
2537     if( !(TessFactor_V_LineDensity > 0) || // NaN will pass
2538         !(TessFactor_U_LineDetail > 0) )
2539     {
2540         m_LastUnRoundedComputedTessFactors[0] = TessFactor_V_LineDensity;
2541         m_LastUnRoundedComputedTessFactors[1] = TessFactor_U_LineDetail;
2542         m_LastComputedTessFactors[0] =
2543         m_LastComputedTessFactors[1] = 0;
2544         return;
2545     }
2546 
2547     CleanupFloatTessFactor(TessFactor_V_LineDensity); // clamp to [1.0f..INF], NaN->1.0f
2548     CleanupFloatTessFactor(TessFactor_U_LineDetail); // clamp to [1.0f..INF], NaN->1.0f
2549 
2550     ClampTessFactor(TessFactor_U_LineDetail); // clamp unbounded user input based on tessellation mode
2551 
2552     m_LastUnRoundedComputedTessFactors[1] = TessFactor_U_LineDetail;    // Save off TessFactors so they can be returned to app
2553 
2554     if(Pow2Partitioning()||IntegerPartitioning())
2555     {
2556         RoundUpTessFactor(TessFactor_U_LineDetail);
2557     }
2558 
2559     OverridePartitioning(PIPE_TESSELLATOR_PARTITIONING_INTEGER);
2560 
2561     ClampTessFactor(TessFactor_V_LineDensity); // Clamp unbounded user input to integer
2562     m_LastUnRoundedComputedTessFactors[0] = TessFactor_V_LineDensity;    // Save off TessFactors so they can be returned to app
2563 
2564     RoundUpTessFactor(TessFactor_V_LineDensity);
2565 
2566     RestorePartitioning();
2567 
2568     // Save off TessFactors so they can be returned to app
2569     m_LastComputedTessFactors[0] = TessFactor_V_LineDensity;
2570     m_LastComputedTessFactors[1] = TessFactor_U_LineDetail;
2571 }
2572 
2573 //---------------------------------------------------------------------------------------------------------------------------------
2574 // CHLSLTessellator::ClampTessFactor()
2575 //---------------------------------------------------------------------------------------------------------------------------------
ClampTessFactor(float & TessFactor)2576 void CHLSLTessellator::ClampTessFactor(float& TessFactor)
2577 {
2578     if( Pow2Partitioning() )
2579     {
2580         TessFactor = tess_fmin( PIPE_TESSELLATOR_MAX_EVEN_TESSELLATION_FACTOR, tess_fmax( TessFactor, PIPE_TESSELLATOR_MIN_ODD_TESSELLATION_FACTOR) );
2581     }
2582     else if( IntegerPartitioning() )
2583     {
2584         TessFactor = tess_fmin( PIPE_TESSELLATOR_MAX_TESSELLATION_FACTOR, tess_fmax( TessFactor, PIPE_TESSELLATOR_MIN_ODD_TESSELLATION_FACTOR) );
2585     }
2586     else if( Odd() )
2587     {
2588         TessFactor = tess_fmin( PIPE_TESSELLATOR_MAX_ODD_TESSELLATION_FACTOR, tess_fmax( TessFactor, PIPE_TESSELLATOR_MIN_ODD_TESSELLATION_FACTOR) );
2589     }
2590     else // even
2591     {
2592         TessFactor = tess_fmin( PIPE_TESSELLATOR_MAX_EVEN_TESSELLATION_FACTOR, tess_fmax( TessFactor, PIPE_TESSELLATOR_MIN_EVEN_TESSELLATION_FACTOR) );
2593     }
2594 }
2595 
2596 //---------------------------------------------------------------------------------------------------------------------------------
2597 // CHLSLTessellator::CleanupFloatTessFactor()
2598 //---------------------------------------------------------------------------------------------------------------------------------
2599 static const int exponentMask = 0x7f800000;
2600 static const int mantissaMask = 0x007fffff;
CleanupFloatTessFactor(float & input)2601 void CHLSLTessellator::CleanupFloatTessFactor(float& input)
2602 {
2603     // If input is < 1.0f or NaN, clamp to 1.0f.
2604     // In other words, clamp input to [1.0f...+INF]
2605     int bits = *(int*)&input;
2606     if( ( ( ( bits & exponentMask ) == exponentMask ) && ( bits & mantissaMask ) ) ||// nan?
2607         (input < 1.0f) )
2608     {
2609         input = 1;
2610     }
2611 }
2612 
2613 //---------------------------------------------------------------------------------------------------------------------------------
2614 // CHLSLTessellator::ClampFloatTessFactorScale()
2615 //---------------------------------------------------------------------------------------------------------------------------------
ClampFloatTessFactorScale(float & input)2616 void CHLSLTessellator::ClampFloatTessFactorScale(float& input)
2617 {
2618     // If input is < 0.0f or NaN, clamp to 0.0f.  > 1 clamps to 1.
2619     // In other words, clamp input to [0.0f...1.0f]
2620     int bits = *(int*)&input;
2621     if( ( ( ( bits & exponentMask ) == exponentMask ) && ( bits & mantissaMask ) ) ||// nan?
2622         (input < 0.0f) )
2623     {
2624         input = 0;
2625     }
2626     else if( input > 1 )
2627     {
2628         input = 1;
2629     }
2630 }
2631 
2632 //---------------------------------------------------------------------------------------------------------------------------------
2633 // CHLSLTessellator::RoundUpTessFactor()
2634 //---------------------------------------------------------------------------------------------------------------------------------
2635 static const int exponentLSB = 0x00800000;
RoundUpTessFactor(float & TessFactor)2636 void CHLSLTessellator::RoundUpTessFactor(float& TessFactor)
2637 {
2638     // Assume TessFactor is in [1.0f..+INF]
2639     if( Pow2Partitioning() )
2640     {
2641         int bits = *(int*)&TessFactor;
2642         if( bits & mantissaMask )
2643         {
2644             *(int*)&TessFactor = (bits & exponentMask) + exponentLSB;
2645         }
2646     }
2647     else if( IntegerPartitioning() )
2648     {
2649         TessFactor = ceil(TessFactor);
2650     }
2651 }
2652