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