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