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