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