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