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