swr/rasterizer: Add tessellator implementation to the rasterizer
authorJan Zielinski <jan.zielinski@intel.com>
Wed, 4 Dec 2019 12:10:18 +0000 (13:10 +0100)
committerJan Zielinski <jan.zielinski@intel.com>
Wed, 11 Dec 2019 16:54:37 +0000 (16:54 +0000)
This is initial commit on the way to implement ARB_tessellation_shader
extension in OpenSWR. It introduces tessellator implementation
taken from Microsoft GitHub (published under MIT license):

https://github.com/microsoft/DirectX-Specs/blob/master/d3d/archive/images/d3d11/tessellator.cpp
https://github.com/microsoft/DirectX-Specs/blob/master/d3d/archive/images/d3d11/tessellator.hpp

It also adds some glue code that connects the tessellator
to the internals of SWR rasterizer.

Acked-by: Dave Airlie <airlied@redhat.com>
Acked-by: Bruce Cherniak <bruce.cherniak@intel.com>
Reviwed-by: Alok Hota <alok.hota@intel.com>
src/gallium/drivers/swr/Makefile.sources
src/gallium/drivers/swr/meson.build
src/gallium/drivers/swr/rasterizer/core/tessellator.cpp [new file with mode: 0644]
src/gallium/drivers/swr/rasterizer/core/tessellator.h
src/gallium/drivers/swr/rasterizer/core/tessellator.hpp [new file with mode: 0644]

index 720bd590d82ef0581e018f917319c4dcc37f697d..2f199ae348e69daebbab91c5e2c1c6a8160a9e56 100644 (file)
@@ -128,6 +128,8 @@ CORE_CXX_SOURCES := \
        rasterizer/core/state.h \
        rasterizer/core/state_funcs.h \
        rasterizer/core/tessellator.h \
+       rasterizer/core/tessellator.hpp \
+       rasterizer/core/tessellator.cpp \
        rasterizer/core/threads.cpp \
        rasterizer/core/threads.h \
        rasterizer/core/tilemgr.cpp \
index 2fad60365a36f1d0fccf1bd214421f5234fb58e6..5a5c46dcbddb9de5e8d85c60bc20f43e70bf09d0 100644 (file)
@@ -129,6 +129,8 @@ files_swr_arch = files(
   'rasterizer/core/state.h',
   'rasterizer/core/state_funcs.h',
   'rasterizer/core/tessellator.h',
+  'rasterizer/core/tessellator.hpp',
+  'rasterizer/core/tessellator.cpp',
   'rasterizer/core/threads.cpp',
   'rasterizer/core/threads.h',
   'rasterizer/core/tilemgr.cpp',
@@ -316,5 +318,5 @@ libmesaswr = static_library(
 
 driver_swr = declare_dependency(
   compile_args : '-DGALLIUM_SWR',
-  link_with : libmesaswr,
+  link_with : libmesaswr
 )
diff --git a/src/gallium/drivers/swr/rasterizer/core/tessellator.cpp b/src/gallium/drivers/swr/rasterizer/core/tessellator.cpp
new file mode 100644 (file)
index 0000000..6e12b69
--- /dev/null
@@ -0,0 +1,2693 @@
+/*
+    Copyright (c) Microsoft Corporation
+
+    Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
+    associated documentation files (the "Software"), to deal in the Software without restriction,
+    including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+    and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+    subject to the following conditions:
+
+    The above copyright notice and this permission notice shall be included in all copies or substantial
+    portions of the Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
+    NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+    IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+    WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+    SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include "tessellator.hpp"
+// #include <math.h> // ceil
+#include <cmath>
+//#include <windows.h> // Just used for some commented out debug stat printing.
+//#include <strsafe.h> // Ditto.
+#define min(x,y) (x < y ? x : y)
+#define max(x,y) (x > y ? x : y)
+
+//=================================================================================================================================
+// Some D3D Compliant Float Math (reference rasterizer implements these in RefALU class)
+//=================================================================================================================================
+//
+//---------------------------------------------------------------------------------------------------------------------------------
+// isNaN
+//---------------------------------------------------------------------------------------------------------------------------------
+bool isNaN( float a )
+{
+    static const int exponentMask = 0x7f800000;
+    static const int mantissaMask = 0x007fffff;
+    int u = *(int*)&a;
+    return ( ( ( u & exponentMask ) == exponentMask ) && ( u & mantissaMask ) ); // NaN
+}
+
+//---------------------------------------------------------------------------------------------------------------------------------
+// flush (denorm)
+//---------------------------------------------------------------------------------------------------------------------------------
+float flush( float a )
+{
+    static const int minNormalizedFloat = 0x00800000;
+    static const int signBit = 0x80000000;
+    static const int signBitComplement = 0x7fffffff;
+    int b = (*(int*)&a) & signBitComplement; // fabs()
+    if( b < minNormalizedFloat ) // UINT comparison. NaN/INF do test false here
+    {
+        b = signBit & (*(int*)&a);
+        return *(float*)&b;
+    }
+    return a;
+}
+
+//---------------------------------------------------------------------------------------------------------------------------------
+// IEEE754R min
+//---------------------------------------------------------------------------------------------------------------------------------
+float fmin( float a, float b )
+{
+    float _a = flush( a );
+    float _b = flush( b );
+    if( isNaN( _b ) )
+    {
+        return a;
+    }
+    else if( ( _a == 0 ) && ( _b == 0 ) )
+    {
+        return ( (*(int*)&_a) & 0x80000000 ) ? a : b;
+    }
+    return _a < _b ? a : b;
+}
+
+//---------------------------------------------------------------------------------------------------------------------------------
+// IEEE754R max
+//---------------------------------------------------------------------------------------------------------------------------------
+float fmax( float a, float b )
+{
+    float _a = flush( a );
+    float _b = flush( b );
+
+    if( isNaN( _b ) )
+    {
+        return a;
+    }
+    else if( ( _a == 0 ) && ( _b == 0 ) )
+    {
+        return ( (*(int*)&_b) & 0x80000000 ) ? a : b;
+    }
+    return _a >= _b ? a : b;
+}
+
+//=================================================================================================================================
+// Fixed Point Math
+//=================================================================================================================================
+
+//-----------------------------------------------------------------------------------------------------------------------------
+// floatToFixedPoint
+//
+// Convert 32-bit float to 32-bit fixed point integer, using only
+// integer arithmetic + bitwise operations.
+//
+// c_uIBits:  UINT8     : Width of i (aka. integer bits)
+// c_uFBits:  UINT8     : Width of f (aka. fractional bits)
+// c_bSigned: bool      : Whether the integer bits are a 2's complement signed value
+// input:     float     : All values valid.
+// output:    INT32     : At most 24 bits from LSB are meaningful, depending
+//                        on the fixed point bit representation chosen (see
+//                        below).  Extra bits are sign extended from the most
+//                        meaningful bit.
+//
+//-----------------------------------------------------------------------------------------------------------------------------
+
+typedef unsigned char UINT8;
+typedef int INT32;
+template< const UINT8 c_uIBits, const UINT8 c_uFBits, const bool c_bSigned >
+INT32 floatToIDotF( const float& input )
+{
+    // ------------------------------------------------------------------------
+    //                                                output fixed point format
+    // 32-bit result:
+    //
+    //      [sign-extend]i.f
+    //      |              |
+    //      MSB(31)...LSB(0)
+    //
+    //      f               fractional part of the number, an unsigned
+    //                      value with _fxpFracBitCount bits (defined below)
+    //
+    //      .               implied decimal
+    //
+    //      i               integer part of the number, a 2's complement
+    //                      value with _fxpIntBitCount bits (defined below)
+    //
+    //      [sign-extend]   MSB of i conditionally replicated
+    //
+    // ------------------------------------------------------------------------
+    // Define fixed point bit counts
+    //
+
+    // Commenting out C_ASSERT below to minimise #includes:
+    // C_ASSERT( 2 <= c_uIBits && c_uIBits <= 32 && c_uFBits <= 32 && c_uIBits + c_uFBits <= 32 );
+
+    // Define most negative and most positive fixed point values
+    const INT32 c_iMinResult = (c_bSigned ? INT32( -1 ) << (c_uIBits + c_uFBits - 1) : 0);
+    const INT32 c_iMaxResult = ~c_iMinResult;
+
+    // ------------------------------------------------------------------------
+    //                                                constant float properties
+    // ------------------------------------------------------------------------
+    const UINT8 _fltMantissaBitCount = 23;
+    const UINT8 _fltExponentBitCount = 8;
+    const INT32 _fltExponentBias     = (INT32( 1 ) << (_fltExponentBitCount - 1)) - 1;
+    const INT32 _fltHiddenBit        = INT32( 1 ) << _fltMantissaBitCount;
+    const INT32 _fltMantissaMask     = _fltHiddenBit - 1;
+    const INT32 _fltExponentMask     = ((INT32( 1 ) << _fltExponentBitCount) - 1) << _fltMantissaBitCount;
+    const INT32 _fltSignBit          = INT32( 1 ) << (_fltExponentBitCount + _fltMantissaBitCount);
+
+    // ------------------------------------------------------------------------
+    //              define min and max values as floats (clamp to these bounds)
+    // ------------------------------------------------------------------------
+    INT32 _fxpMaxPosValueFloat;
+    INT32 _fxpMaxNegValueFloat;
+
+    if (c_bSigned)
+    {
+        // The maximum positive fixed point value is 2^(i-1) - 2^(-f).
+        // The following constructs the floating point bit pattern for this value,
+        // as long as i >= 2.
+        _fxpMaxPosValueFloat = (_fltExponentBias + c_uIBits - 1) <<_fltMantissaBitCount;
+        const INT32 iShift = _fltMantissaBitCount + 2 - c_uIBits - c_uFBits;
+        if (iShift >= 0)
+        {
+//            assert( iShift < 32 );
+#pragma warning( suppress : 4293 )
+            _fxpMaxPosValueFloat -= INT32( 1 ) << iShift;
+        }
+
+        // The maximum negative fixed point value is -2^(i-1).
+        // The following constructs the floating point bit pattern for this value,
+        // as long as i >= 2.
+        // We need this number without the sign bit
+        _fxpMaxNegValueFloat = (_fltExponentBias + c_uIBits - 1) << _fltMantissaBitCount;
+    }
+    else
+    {
+        // The maximum positive fixed point value is 2^(i) - 2^(-f).
+        // The following constructs the floating point bit pattern for this value,
+        // as long as i >= 2.
+        _fxpMaxPosValueFloat = (_fltExponentBias + c_uIBits) <<_fltMantissaBitCount;
+        const INT32 iShift = _fltMantissaBitCount + 1 - c_uIBits - c_uFBits;
+        if (iShift >= 0)
+        {
+//            assert( iShift < 32 );
+#pragma warning( suppress : 4293 )
+            _fxpMaxPosValueFloat -= INT32( 1 ) << iShift;
+        }
+
+        // The maximum negative fixed point value is 0.
+        _fxpMaxNegValueFloat = 0;
+    }
+
+    // ------------------------------------------------------------------------
+    //                                                float -> fixed conversion
+    // ------------------------------------------------------------------------
+
+    // ------------------------------------------------------------------------
+    //                                                      examine input float
+    // ------------------------------------------------------------------------
+    INT32 output              = *(INT32*)&input;
+    INT32 unbiasedExponent    = ((output & _fltExponentMask) >> _fltMantissaBitCount) - _fltExponentBias;
+    INT32 isNegative          = output & _fltSignBit;
+
+    // ------------------------------------------------------------------------
+    //                                                                      nan
+    // ------------------------------------------------------------------------
+    if (unbiasedExponent == (_fltExponentBias + 1) && (output & _fltMantissaMask))
+    {
+        // nan converts to 0
+        output = 0;
+    }
+    // ------------------------------------------------------------------------
+    //                                                       too large positive
+    // ------------------------------------------------------------------------
+    else if (!isNegative && output >= _fxpMaxPosValueFloat) // integer compare
+    {
+        output = c_iMaxResult;
+    }
+    // ------------------------------------------------------------------------
+    //                                                       too large negative
+    // ------------------------------------------------------------------------
+                                            // integer compare
+    else if (isNegative && (output & ~_fltSignBit) >= _fxpMaxNegValueFloat)
+    {
+        output = c_iMinResult;
+    }
+    // ------------------------------------------------------------------------
+    //                                                                too small
+    // ------------------------------------------------------------------------
+    else if (unbiasedExponent < -c_uFBits - 1)
+    {
+        // clamp to 0
+        output = 0;
+    }
+    // ------------------------------------------------------------------------
+    //                                                             within range
+    // ------------------------------------------------------------------------
+    else
+    {
+        // copy mantissa, add hidden bit
+        output = (output & _fltMantissaMask) | _fltHiddenBit;
+
+        INT32 extraBits = _fltMantissaBitCount - c_uFBits - unbiasedExponent;
+        if (extraBits >= 0)
+        {
+            // 2's complement if negative
+            if (isNegative)
+            {
+                output = ~output + 1;
+            }
+
+            // From the range checks that led here, it is known that
+            // unbiasedExponent < c_uIBits.  So, at most:
+            // (a) unbiasedExponent == c_uIBits - 1.
+            //
+            // From compile validation above, it is known that
+            // c_uIBits + c_uFBits <= _fltMantissaBitCount + 1).
+            // So, at minimum:
+            // (b) _fltMantissaBitCount == _fxtIntBitCount + c_uFBits - 1
+            //
+            // Substituting (a) and (b) into extraBits calculation above:
+            // extraBits >= (_fxtIntBitCount + c_uFBits - 1)
+            //              - c_uFBits - (c_uIBits - 1)
+            // extraBits >= 0
+            //
+            // Thus we only have to worry about shifting right by 0 or more
+            // bits to get the decimal to the right place, and never have
+            // to shift left.
+
+            INT32 LSB             = 1 << extraBits; // last bit being kept
+            INT32 extraBitsMask   = LSB - 1;
+            INT32 half            = LSB >> 1; // round bias
+
+            // round to nearest-even at LSB
+            if ((output & LSB) || (output & extraBitsMask) > half)
+            {
+                output += half;
+            }
+
+            // shift off the extra bits (sign extending)
+            output >>= extraBits;
+        }
+        else
+        {
+            output <<= -extraBits;
+
+            // 2's complement if negative
+            if (isNegative)
+            {
+                output = ~output + 1;
+            }
+        }
+    }
+    return output;
+}
+//-----------------------------------------------------------------------------------------------------------------------------
+
+#define FXP_INTEGER_BITS 15
+#define FXP_FRACTION_BITS 16
+#define FXP_FRACTION_MASK 0x0000ffff
+#define FXP_INTEGER_MASK 0x7fff0000
+#define FXP_THREE (3<<FXP_FRACTION_BITS)
+#define FXP_ONE (1<<FXP_FRACTION_BITS)
+#define FXP_ONE_THIRD 0x00005555
+#define FXP_TWO_THIRDS 0x0000aaaa
+#define FXP_ONE_HALF   0x00008000
+
+#define FXP_MAX_INPUT_TESS_FACTOR_BEFORE_TRIPLE_AVERAGE 0x55540000 // 1/3 of max fixed point number - 1.  Numbers less than
+                                                    // or equal to this allows avg. reduction on a tri patch
+                                                    // including rounding.
+
+#define FXP_MAX_INPUT_TESS_FACTOR_BEFORE_PAIR_AVERAGE 0x7FFF0000 // 1/2 of max fixed point number - 1.  Numbers less than
+                                                    // or equal to this allows avg. reduction on a quad patch
+                                                    // including rounding.
+
+static const FXP s_fixedReciprocal[D3D11_TESSELLATOR_MAX_TESSELLATION_FACTOR+1] =
+{
+    0xffffffff, // 1/0 is the first entry (unused)
+    0x10000, 0x8000, 0x5555, 0x4000,
+    0x3333, 0x2aab, 0x2492, 0x2000,
+    0x1c72, 0x199a, 0x1746, 0x1555,
+    0x13b1, 0x1249, 0x1111, 0x1000,
+    0xf0f, 0xe39, 0xd79, 0xccd,
+    0xc31, 0xba3, 0xb21, 0xaab,
+    0xa3d, 0x9d9, 0x97b, 0x925,
+    0x8d4, 0x889, 0x842, 0x800,
+    0x7c2, 0x788, 0x750, 0x71c,
+    0x6eb, 0x6bd, 0x690, 0x666,
+    0x63e, 0x618, 0x5f4, 0x5d1,
+    0x5b0, 0x591, 0x572, 0x555,
+    0x539, 0x51f, 0x505, 0x4ec,
+    0x4d5, 0x4be, 0x4a8, 0x492,
+    0x47e, 0x46a, 0x457, 0x444,
+    0x432, 0x421, 0x410, 0x400, // 1/64 is the last entry
+};
+
+#define FLOAT_THREE 3.0f
+#define FLOAT_ONE 1.0f
+
+//---------------------------------------------------------------------------------------------------------------------------------
+// floatToFixed
+//---------------------------------------------------------------------------------------------------------------------------------
+FXP floatToFixed(const float& input)
+{
+    return floatToIDotF< FXP_INTEGER_BITS, FXP_FRACTION_BITS, /*bSigned*/false >( input );
+}
+
+//---------------------------------------------------------------------------------------------------------------------------------
+// fixedToFloat
+//---------------------------------------------------------------------------------------------------------------------------------
+float fixedToFloat(const FXP& input)
+{
+    // not worrying about denorm flushing the float operations (the DX spec behavior for div), since the numbers will not be that small during tessellation.
+    return ((float)(input>>FXP_FRACTION_BITS) + (float)(input&FXP_FRACTION_MASK)/(1<<FXP_FRACTION_BITS));
+}
+
+//---------------------------------------------------------------------------------------------------------------------------------
+// isEven
+//---------------------------------------------------------------------------------------------------------------------------------
+bool isEven(const float& input)
+{
+    return (((int)input) & 1) ? false : true;
+}
+
+//---------------------------------------------------------------------------------------------------------------------------------
+// fxpCeil
+//---------------------------------------------------------------------------------------------------------------------------------
+FXP fxpCeil(const FXP& input)
+{
+    if( input & FXP_FRACTION_MASK )
+    {
+        return (input & FXP_INTEGER_MASK) + FXP_ONE;
+    }
+    return input;
+}
+
+//---------------------------------------------------------------------------------------------------------------------------------
+// fxpFloor
+//---------------------------------------------------------------------------------------------------------------------------------
+FXP fxpFloor(const FXP& input)
+{
+    return (input & FXP_INTEGER_MASK);
+}
+
+//=================================================================================================================================
+// CHWTessellator
+//=================================================================================================================================
+
+//---------------------------------------------------------------------------------------------------------------------------------
+// CHWTessellator::CHWTessellator
+//---------------------------------------------------------------------------------------------------------------------------------
+CHWTessellator::CHWTessellator()
+{
+    m_Point = 0;
+    m_Index = 0;
+    m_NumPoints = 0;
+    m_NumIndices = 0;
+    m_bUsingPatchedIndices = false;
+    m_bUsingPatchedIndices2 = false;
+#ifdef ALLOW_XBOX_360_COMPARISON
+       m_bXBox360Mode = false;
+#endif
+}
+//---------------------------------------------------------------------------------------------------------------------------------
+// CHWTessellator::~CHWTessellator
+//---------------------------------------------------------------------------------------------------------------------------------
+CHWTessellator::~CHWTessellator()
+{
+    delete [] m_Point;
+    delete [] m_Index;
+}
+
+//---------------------------------------------------------------------------------------------------------------------------------
+// CHWTessellator::Init
+// User calls this.
+//---------------------------------------------------------------------------------------------------------------------------------
+void CHWTessellator::Init(
+    D3D11_TESSELLATOR_PARTITIONING       partitioning,
+    D3D11_TESSELLATOR_OUTPUT_PRIMITIVE   outputPrimitive)
+{
+    if( 0 == m_Point )
+    {
+        m_Point = new DOMAIN_POINT[MAX_POINT_COUNT];
+    }
+    if( 0 == m_Index )
+    {
+        m_Index = new int[MAX_INDEX_COUNT];
+    }
+    m_partitioning = partitioning;
+    m_originalPartitioning = partitioning;
+    switch( partitioning )
+    {
+    case D3D11_TESSELLATOR_PARTITIONING_INTEGER:
+    default:
+        break;
+    case D3D11_TESSELLATOR_PARTITIONING_FRACTIONAL_ODD:
+        m_parity = TESSELLATOR_PARITY_ODD;
+        break;
+    case D3D11_TESSELLATOR_PARTITIONING_FRACTIONAL_EVEN:
+        m_parity = TESSELLATOR_PARITY_EVEN;
+        break;
+    }
+    m_originalParity = m_parity;
+    m_outputPrimitive = outputPrimitive;
+    m_NumPoints = 0;
+    m_NumIndices = 0;
+}
+//---------------------------------------------------------------------------------------------------------------------------------
+// CHWTessellator::TessellateQuadDomain
+// User calls this
+//---------------------------------------------------------------------------------------------------------------------------------
+void CHWTessellator::TessellateQuadDomain( float tessFactor_Ueq0, float tessFactor_Veq0, float tessFactor_Ueq1, float tessFactor_Veq1,
+                                         float insideTessFactor_U, float insideTessFactor_V )
+{
+    PROCESSED_TESS_FACTORS_QUAD processedTessFactors;
+    QuadProcessTessFactors(tessFactor_Ueq0,tessFactor_Veq0,tessFactor_Ueq1,tessFactor_Veq1,insideTessFactor_U,insideTessFactor_V,processedTessFactors);
+
+    if( processedTessFactors.bPatchCulled )
+    {
+        m_NumPoints = 0;
+        m_NumIndices = 0;
+        return;
+    }
+    else if( processedTessFactors.bJustDoMinimumTessFactor )
+    {
+        DefinePoint(/*U*/0,/*V*/0,/*pointStorageOffset*/0);
+        DefinePoint(/*U*/FXP_ONE,/*V*/0,/*pointStorageOffset*/1);
+        DefinePoint(/*U*/FXP_ONE,/*V*/FXP_ONE,/*pointStorageOffset*/2);
+        DefinePoint(/*U*/0,/*V*/FXP_ONE,/*pointStorageOffset*/3);
+        m_NumPoints = 4;
+
+        switch(m_outputPrimitive)
+        {
+        case D3D11_TESSELLATOR_OUTPUT_TRIANGLE_CW:
+        case D3D11_TESSELLATOR_OUTPUT_TRIANGLE_CCW:
+            // function orients them CCW if needed
+            DefineClockwiseTriangle(0,1,3,/*indexStorageOffset*/0);
+            DefineClockwiseTriangle(1,2,3,/*indexStorageOffset*/3);
+            m_NumIndices = 6;
+            break;
+        case D3D11_TESSELLATOR_OUTPUT_POINT:
+            DumpAllPoints();
+            break;
+        case D3D11_TESSELLATOR_OUTPUT_LINE:
+            DumpAllPointsAsInOrderLineList();
+            break;
+        }
+        return;
+    }
+
+    QuadGeneratePoints(processedTessFactors);
+
+    if( m_outputPrimitive == D3D11_TESSELLATOR_OUTPUT_POINT )
+    {
+        DumpAllPoints();
+        return;
+    }
+    if( m_outputPrimitive == D3D11_TESSELLATOR_OUTPUT_LINE )
+    {
+        DumpAllPointsAsInOrderLineList();
+        return;
+    }
+
+    QuadGenerateConnectivity(processedTessFactors); // can be done in parallel to QuadGeneratePoints()
+}
+
+//---------------------------------------------------------------------------------------------------------------------------------
+// CHWTessellator::QuadProcessTessFactors
+//---------------------------------------------------------------------------------------------------------------------------------
+void CHWTessellator::QuadProcessTessFactors( float tessFactor_Ueq0, float tessFactor_Veq0, float tessFactor_Ueq1, float tessFactor_Veq1,
+                      float insideTessFactor_U, float insideTessFactor_V, PROCESSED_TESS_FACTORS_QUAD& processedTessFactors )
+{
+    // Is the patch culled?
+    if( !(tessFactor_Ueq0 > 0) || // NaN will pass
+        !(tessFactor_Veq0 > 0) ||
+        !(tessFactor_Ueq1 > 0) ||
+        !(tessFactor_Veq1 > 0) )
+    {
+        processedTessFactors.bPatchCulled = true;
+        return;
+    }
+    else
+    {
+        processedTessFactors.bPatchCulled = false;
+    }
+
+    // Clamp edge TessFactors
+    float lowerBound, upperBound;
+    switch(m_originalPartitioning)
+    {
+        case D3D11_TESSELLATOR_PARTITIONING_INTEGER:
+        case D3D11_TESSELLATOR_PARTITIONING_POW2: // don�t care about pow2 distinction for validation, just treat as integer
+            lowerBound = D3D11_TESSELLATOR_MIN_ODD_TESSELLATION_FACTOR;
+            upperBound = D3D11_TESSELLATOR_MAX_EVEN_TESSELLATION_FACTOR;
+            break;
+
+        case D3D11_TESSELLATOR_PARTITIONING_FRACTIONAL_EVEN:
+            lowerBound = D3D11_TESSELLATOR_MIN_EVEN_TESSELLATION_FACTOR;
+            upperBound = D3D11_TESSELLATOR_MAX_EVEN_TESSELLATION_FACTOR;
+            break;
+
+        case D3D11_TESSELLATOR_PARTITIONING_FRACTIONAL_ODD:
+            lowerBound = D3D11_TESSELLATOR_MIN_ODD_TESSELLATION_FACTOR;
+            upperBound = D3D11_TESSELLATOR_MAX_ODD_TESSELLATION_FACTOR;
+            break;
+    }
+
+    tessFactor_Ueq0 = fmin( upperBound, fmax( lowerBound, tessFactor_Ueq0 ) );
+    tessFactor_Veq0 = fmin( upperBound, fmax( lowerBound, tessFactor_Veq0 ) );
+    tessFactor_Ueq1 = fmin( upperBound, fmax( lowerBound, tessFactor_Ueq1 ) );
+    tessFactor_Veq1 = fmin( upperBound, fmax( lowerBound, tessFactor_Veq1 ) );
+
+    if( HWIntegerPartitioning()) // pow2 or integer, round to next int (hw doesn't care about pow2 distinction)
+    {
+        tessFactor_Ueq0 = ceil(tessFactor_Ueq0);
+        tessFactor_Veq0 = ceil(tessFactor_Veq0);
+        tessFactor_Ueq1 = ceil(tessFactor_Ueq1);
+        tessFactor_Veq1 = ceil(tessFactor_Veq1);
+    }
+
+    // Clamp inside TessFactors
+    if(D3D11_TESSELLATOR_PARTITIONING_FRACTIONAL_ODD == m_originalPartitioning)
+    {
+#define EPSILON 0.0000152587890625f // 2^(-16), min positive fixed point fraction
+#define MIN_ODD_TESSFACTOR_PLUS_HALF_EPSILON (D3D11_TESSELLATOR_MIN_ODD_TESSELLATION_FACTOR + EPSILON/2)
+        // If any TessFactor will end up > 1 after floatToFixed conversion later,
+        // then force the inside TessFactors to be > 1 so there is a picture frame.
+        if( (tessFactor_Ueq0 > MIN_ODD_TESSFACTOR_PLUS_HALF_EPSILON) ||
+            (tessFactor_Veq0 > MIN_ODD_TESSFACTOR_PLUS_HALF_EPSILON) ||
+            (tessFactor_Ueq1 > MIN_ODD_TESSFACTOR_PLUS_HALF_EPSILON) ||
+            (tessFactor_Veq1 > MIN_ODD_TESSFACTOR_PLUS_HALF_EPSILON) ||
+            (insideTessFactor_U > MIN_ODD_TESSFACTOR_PLUS_HALF_EPSILON) ||
+            (insideTessFactor_V > MIN_ODD_TESSFACTOR_PLUS_HALF_EPSILON) )
+        {
+            // Force picture frame
+            lowerBound = D3D11_TESSELLATOR_MIN_ODD_TESSELLATION_FACTOR + EPSILON;
+        }
+    }
+
+    insideTessFactor_U = fmin( upperBound, fmax( lowerBound, insideTessFactor_U ) );
+    insideTessFactor_V = fmin( upperBound, fmax( lowerBound, insideTessFactor_V ) );
+    // Note the above clamps map NaN to lowerBound
+
+
+    if( HWIntegerPartitioning()) // pow2 or integer, round to next int (hw doesn't care about pow2 distinction)
+    {
+        insideTessFactor_U = ceil(insideTessFactor_U);
+        insideTessFactor_V = ceil(insideTessFactor_V);
+    }
+
+    // Reset our vertex and index buffers.  We have enough storage for the max tessFactor.
+    m_NumPoints = 0;
+    m_NumIndices = 0;
+
+    // Process tessFactors
+    float outsideTessFactor[QUAD_EDGES] = {tessFactor_Ueq0, tessFactor_Veq0, tessFactor_Ueq1, tessFactor_Veq1};
+    float insideTessFactor[QUAD_AXES] = {insideTessFactor_U,insideTessFactor_V};
+    int edge, axis;
+    if( HWIntegerPartitioning() )
+    {
+        for( edge = 0; edge < QUAD_EDGES; edge++ )
+        {
+            int edgeEven = isEven(outsideTessFactor[edge]);
+            processedTessFactors.outsideTessFactorParity[edge] = edgeEven ? TESSELLATOR_PARITY_EVEN : TESSELLATOR_PARITY_ODD;
+        }
+        for( axis = 0; axis < QUAD_AXES; axis++ )
+        {
+            processedTessFactors.insideTessFactorParity[axis] =
+                (isEven(insideTessFactor[axis]) || (FLOAT_ONE == insideTessFactor[axis]) )
+                ? TESSELLATOR_PARITY_EVEN : TESSELLATOR_PARITY_ODD;
+        }
+    }
+    else
+    {
+        for( edge = 0; edge < QUAD_EDGES; edge++ )
+        {
+            processedTessFactors.outsideTessFactorParity[edge] = m_originalParity;
+        }
+        processedTessFactors.insideTessFactorParity[U] = processedTessFactors.insideTessFactorParity[V] = m_originalParity;
+    }
+
+    // Save fixed point TessFactors
+    for( edge = 0; edge < QUAD_EDGES; edge++ )
+    {
+        processedTessFactors.outsideTessFactor[edge] = floatToFixed(outsideTessFactor[edge]);
+    }
+    for( axis = 0; axis < QUAD_AXES; axis++ )
+    {
+        processedTessFactors.insideTessFactor[axis] = floatToFixed(insideTessFactor[axis]);
+    }
+
+    if( HWIntegerPartitioning() || Odd() )
+    {
+        // Special case if all TessFactors are 1
+        if( (FXP_ONE == processedTessFactors.insideTessFactor[U]) &&
+            (FXP_ONE == processedTessFactors.insideTessFactor[V]) &&
+            (FXP_ONE == processedTessFactors.outsideTessFactor[Ueq0]) &&
+            (FXP_ONE == processedTessFactors.outsideTessFactor[Veq0]) &&
+            (FXP_ONE == processedTessFactors.outsideTessFactor[Ueq1]) &&
+            (FXP_ONE == processedTessFactors.outsideTessFactor[Veq1]) )
+        {
+            processedTessFactors.bJustDoMinimumTessFactor = true;
+            return;
+        }
+    }
+    processedTessFactors.bJustDoMinimumTessFactor = false;
+
+    // Compute TessFactor-specific metadata
+    for(int edge = 0; edge < QUAD_EDGES; edge++ )
+    {
+        SetTessellationParity(processedTessFactors.outsideTessFactorParity[edge]);
+        ComputeTessFactorContext(processedTessFactors.outsideTessFactor[edge], processedTessFactors.outsideTessFactorCtx[edge]);
+    }
+
+    for(int axis = 0; axis < QUAD_AXES; axis++)
+    {
+        SetTessellationParity(processedTessFactors.insideTessFactorParity[axis]);
+        ComputeTessFactorContext(processedTessFactors.insideTessFactor[axis], processedTessFactors.insideTessFactorCtx[axis]);
+    }
+
+    // Compute some initial data.
+
+    // outside edge offsets and storage
+    for(int edge = 0; edge < QUAD_EDGES; edge++ )
+    {
+        SetTessellationParity(processedTessFactors.outsideTessFactorParity[edge]);
+        processedTessFactors.numPointsForOutsideEdge[edge] = NumPointsForTessFactor(processedTessFactors.outsideTessFactor[edge]);
+        m_NumPoints += processedTessFactors.numPointsForOutsideEdge[edge];
+    }
+    m_NumPoints -= 4;
+
+    // inside edge offsets
+    for(int axis = 0; axis < QUAD_AXES; axis++)
+    {
+        SetTessellationParity(processedTessFactors.insideTessFactorParity[axis]);
+        processedTessFactors.numPointsForInsideTessFactor[axis] = NumPointsForTessFactor(processedTessFactors.insideTessFactor[axis]);
+        int pointCountMin = ( TESSELLATOR_PARITY_ODD == processedTessFactors.insideTessFactorParity[axis] ) ? 4 : 3;
+        // max() allows degenerate transition regions when inside TessFactor == 1
+        processedTessFactors.numPointsForInsideTessFactor[axis] = max(pointCountMin,processedTessFactors.numPointsForInsideTessFactor[axis]);
+    }
+
+    processedTessFactors.insideEdgePointBaseOffset = m_NumPoints;
+
+    // inside storage, including interior edges above
+    int numInteriorPoints = (processedTessFactors.numPointsForInsideTessFactor[U] - 2)*(processedTessFactors.numPointsForInsideTessFactor[V]-2);
+    m_NumPoints += numInteriorPoints;
+}
+
+//---------------------------------------------------------------------------------------------------------------------------------
+// CHWTessellator::QuadGeneratePoints
+//---------------------------------------------------------------------------------------------------------------------------------
+void CHWTessellator::QuadGeneratePoints( const PROCESSED_TESS_FACTORS_QUAD& processedTessFactors )
+{
+    // Generate exterior ring edge points, clockwise from top-left
+    int pointOffset = 0;
+    int edge;
+    for(edge = 0; edge < QUAD_EDGES; edge++ )
+    {
+        int parity = edge&0x1;
+        int startPoint = 0;
+        int endPoint = processedTessFactors.numPointsForOutsideEdge[edge] - 1;
+        for(int p = startPoint; p < endPoint; p++,pointOffset++) // don't include end, since next edge starts with it.
+        {
+            FXP fxpParam;
+            int q = ((edge==1)||(edge==2)) ? p : endPoint - p; // reverse order
+            SetTessellationParity(processedTessFactors.outsideTessFactorParity[edge]);
+            PlacePointIn1D(processedTessFactors.outsideTessFactorCtx[edge],q,fxpParam);
+            if( parity )
+            {
+                DefinePoint(/*U*/fxpParam,
+                            /*V*/(edge == 3) ? FXP_ONE : 0,
+                            /*pointStorageOffset*/pointOffset);
+            }
+            else
+            {
+                DefinePoint(/*U*/(edge == 2) ? FXP_ONE : 0,
+                            /*V*/fxpParam,
+                            /*pointStorageOffset*/pointOffset);
+            }
+        }
+    }
+
+    // Generate interior ring points, clockwise from (U==0,V==1) (bottom-left) spiralling toward center
+    static const int startRing = 1;
+    int minNumPointsForTessFactor = min(processedTessFactors.numPointsForInsideTessFactor[U],processedTessFactors.numPointsForInsideTessFactor[V]);
+    int numRings = (minNumPointsForTessFactor >> 1);  // note for even tess we aren't counting center point here.
+    for(int ring = startRing; ring < numRings; ring++)
+    {
+        int startPoint = ring;
+        int endPoint[QUAD_AXES] = {processedTessFactors.numPointsForInsideTessFactor[U] - 1 - startPoint,
+                                   processedTessFactors.numPointsForInsideTessFactor[V] - 1 - startPoint};
+
+        for(edge = 0; edge < QUAD_EDGES; edge++ )
+        {
+            int parity[QUAD_AXES] = {edge&0x1,((edge+1)&0x1)};
+            int perpendicularAxisPoint = (edge < 2) ? startPoint : endPoint[parity[0]];
+            FXP fxpPerpParam;
+            SetTessellationParity(processedTessFactors.insideTessFactorParity[parity[0]]);
+            PlacePointIn1D(processedTessFactors.insideTessFactorCtx[parity[0]],perpendicularAxisPoint,fxpPerpParam);
+            SetTessellationParity(processedTessFactors.insideTessFactorParity[parity[1]]);
+            for(int p = startPoint; p < endPoint[parity[1]]; p++, pointOffset++) // don't include end: next edge starts with it.
+            {
+                FXP fxpParam;
+                int q = ((edge == 1)||(edge==2)) ? p : endPoint[parity[1]] - (p - startPoint);
+                PlacePointIn1D(processedTessFactors.insideTessFactorCtx[parity[1]],q,fxpParam);
+                if( parity[1] )
+                {
+                    DefinePoint(/*U*/fxpPerpParam,
+                                /*V*/fxpParam,
+                                /*pointStorageOffset*/pointOffset);
+                }
+                else
+                {
+                    DefinePoint(/*U*/fxpParam,
+                                /*V*/fxpPerpParam,
+                                /*pointStorageOffset*/pointOffset);
+                }
+            }
+        }
+    }
+    // For even tessellation, the inner "ring" is degenerate - a row of points
+    if( (processedTessFactors.numPointsForInsideTessFactor[U] > processedTessFactors.numPointsForInsideTessFactor[V]) &&
+        (TESSELLATOR_PARITY_EVEN == processedTessFactors.insideTessFactorParity[V]) )
+    {
+        int startPoint = numRings;
+        int endPoint = processedTessFactors.numPointsForInsideTessFactor[U] - 1 - startPoint;
+        SetTessellationParity(processedTessFactors.insideTessFactorParity[U]);
+        for( int p = startPoint; p <= endPoint; p++, pointOffset++ )
+        {
+            FXP fxpParam;
+            PlacePointIn1D(processedTessFactors.insideTessFactorCtx[U],p,fxpParam);
+            DefinePoint(/*U*/fxpParam,
+                        /*V*/FXP_ONE_HALF, // middle
+                        /*pointStorageOffset*/pointOffset);
+        }
+    }
+    else if( (processedTessFactors.numPointsForInsideTessFactor[V] >= processedTessFactors.numPointsForInsideTessFactor[U]) &&
+             (TESSELLATOR_PARITY_EVEN == processedTessFactors.insideTessFactorParity[U]) )
+    {
+        int startPoint = numRings;
+        int endPoint;
+        FXP fxpParam;
+        endPoint = processedTessFactors.numPointsForInsideTessFactor[V] - 1 - startPoint;
+        SetTessellationParity(processedTessFactors.insideTessFactorParity[V]);
+        for( int p = endPoint; p >= startPoint; p--, pointOffset++ )
+        {
+            PlacePointIn1D(processedTessFactors.insideTessFactorCtx[V],p,fxpParam);
+            DefinePoint(/*U*/FXP_ONE_HALF, // middle
+                        /*V*/fxpParam,
+                        /*pointStorageOffset*/pointOffset);
+        }
+    }
+}
+//---------------------------------------------------------------------------------------------------------------------------------
+// CHWTessellator::QuadGenerateConnectivity
+//---------------------------------------------------------------------------------------------------------------------------------
+void CHWTessellator::QuadGenerateConnectivity( const PROCESSED_TESS_FACTORS_QUAD& processedTessFactors )
+{
+    // Generate primitives for all the concentric rings, one side at a time for each ring
+    static const int startRing = 1;
+    int numPointRowsToCenter[QUAD_AXES] = {((processedTessFactors.numPointsForInsideTessFactor[U]+1) >> 1),
+                                            ((processedTessFactors.numPointsForInsideTessFactor[V]+1) >> 1)}; // +1 is so even tess includes the center point
+    int numRings = min(numPointRowsToCenter[U],numPointRowsToCenter[V]);
+    int degeneratePointRing[QUAD_AXES] = { // Even partitioning causes degenerate row of points,
+                                           // which results in exceptions to the point ordering conventions
+                                           // when travelling around the rings counterclockwise.
+        (TESSELLATOR_PARITY_EVEN == processedTessFactors.insideTessFactorParity[V]) ? numPointRowsToCenter[V] - 1 : -1,
+        (TESSELLATOR_PARITY_EVEN == processedTessFactors.insideTessFactorParity[U]) ? numPointRowsToCenter[U] - 1 : -1 };
+
+    const TESS_FACTOR_CONTEXT* outsideTessFactorCtx[QUAD_EDGES] = {&processedTessFactors.outsideTessFactorCtx[Ueq0],
+                                                    &processedTessFactors.outsideTessFactorCtx[Veq0],
+                                                    &processedTessFactors.outsideTessFactorCtx[Ueq1],
+                                                    &processedTessFactors.outsideTessFactorCtx[Veq1]};
+    TESSELLATOR_PARITY outsideTessFactorParity[QUAD_EDGES] = {processedTessFactors.outsideTessFactorParity[Ueq0],
+                                                        processedTessFactors.outsideTessFactorParity[Veq0],
+                                                        processedTessFactors.outsideTessFactorParity[Ueq1],
+                                                        processedTessFactors.outsideTessFactorParity[Veq1]};
+    int numPointsForOutsideEdge[QUAD_EDGES] = {processedTessFactors.numPointsForOutsideEdge[Ueq0],
+                                              processedTessFactors.numPointsForOutsideEdge[Veq0],
+                                              processedTessFactors.numPointsForOutsideEdge[Ueq1],
+                                              processedTessFactors.numPointsForOutsideEdge[Veq1]};
+
+    int insideEdgePointBaseOffset = processedTessFactors.insideEdgePointBaseOffset;
+    int outsideEdgePointBaseOffset = 0;
+    int edge;
+    for(int ring = startRing; ring < numRings; ring++)
+    {
+        int numPointsForInsideEdge[QUAD_AXES] = {processedTessFactors.numPointsForInsideTessFactor[U] - 2*ring,
+                                                 processedTessFactors.numPointsForInsideTessFactor[V] - 2*ring};
+
+        int edge0InsidePointBaseOffset = insideEdgePointBaseOffset;
+        int edge0OutsidePointBaseOffset = outsideEdgePointBaseOffset;
+
+        for(edge = 0; edge < QUAD_EDGES; edge++ )
+        {
+            int parity = (edge+1)&0x1;
+
+            int numTriangles = numPointsForInsideEdge[parity] + numPointsForOutsideEdge[edge] - 2;
+            int insideBaseOffset;
+            int outsideBaseOffset;
+            if( edge == 3 ) // We need to patch the indexing so Stitch() can think it sees
+                            // 2 sequentially increasing rows of points, even though we have wrapped around
+                            // to the end of the inner and outer ring's points, so the last point is really
+                            // the first point for the ring.
+                            // We make it so that when Stitch() calls AddIndex(), that function
+                            // will do any necessary index adjustment.
+            {
+                if( ring == degeneratePointRing[parity] )
+                {
+                    m_IndexPatchContext2.baseIndexToInvert = insideEdgePointBaseOffset + 1;
+                    m_IndexPatchContext2.cornerCaseBadValue = outsideEdgePointBaseOffset + numPointsForOutsideEdge[edge] - 1;
+                    m_IndexPatchContext2.cornerCaseReplacementValue = edge0OutsidePointBaseOffset;
+                    m_IndexPatchContext2.indexInversionEndPoint = (m_IndexPatchContext2.baseIndexToInvert << 1) - 1;
+                    insideBaseOffset = m_IndexPatchContext2.baseIndexToInvert;
+                    outsideBaseOffset = outsideEdgePointBaseOffset;
+                    SetUsingPatchedIndices2(true);
+                }
+                else
+                {
+                    m_IndexPatchContext.insidePointIndexDeltaToRealValue    = insideEdgePointBaseOffset;
+                    m_IndexPatchContext.insidePointIndexBadValue            = numPointsForInsideEdge[parity] - 1;
+                    m_IndexPatchContext.insidePointIndexReplacementValue    = edge0InsidePointBaseOffset;
+                    m_IndexPatchContext.outsidePointIndexPatchBase          = m_IndexPatchContext.insidePointIndexBadValue+1; // past inside patched index range
+                    m_IndexPatchContext.outsidePointIndexDeltaToRealValue   = outsideEdgePointBaseOffset
+                                                                                - m_IndexPatchContext.outsidePointIndexPatchBase;
+                    m_IndexPatchContext.outsidePointIndexBadValue           = m_IndexPatchContext.outsidePointIndexPatchBase
+                                                                                + numPointsForOutsideEdge[edge] - 1;
+                    m_IndexPatchContext.outsidePointIndexReplacementValue   = edge0OutsidePointBaseOffset;
+
+                    insideBaseOffset = 0;
+                    outsideBaseOffset = m_IndexPatchContext.outsidePointIndexPatchBase;
+                    SetUsingPatchedIndices(true);
+                }
+            }
+            else if( (edge == 2) && (ring == degeneratePointRing[parity]) )
+            {
+                m_IndexPatchContext2.baseIndexToInvert = insideEdgePointBaseOffset;
+                m_IndexPatchContext2.cornerCaseBadValue = -1; // unused
+                m_IndexPatchContext2.cornerCaseReplacementValue = -1; // unused
+                m_IndexPatchContext2.indexInversionEndPoint = m_IndexPatchContext2.baseIndexToInvert << 1;
+                insideBaseOffset = m_IndexPatchContext2.baseIndexToInvert;
+                outsideBaseOffset = outsideEdgePointBaseOffset;
+                SetUsingPatchedIndices2(true);
+            }
+            else
+            {
+                insideBaseOffset = insideEdgePointBaseOffset;
+                outsideBaseOffset = outsideEdgePointBaseOffset;
+            }
+            if( ring == startRing )
+            {
+                StitchTransition(/*baseIndexOffset: */m_NumIndices,
+                               insideBaseOffset,processedTessFactors.insideTessFactorCtx[parity].numHalfTessFactorPoints,processedTessFactors.insideTessFactorParity[parity],
+                               outsideBaseOffset,outsideTessFactorCtx[edge]->numHalfTessFactorPoints,outsideTessFactorParity[edge]);
+            }
+            else
+            {
+                StitchRegular(/*bTrapezoid*/true, DIAGONALS_MIRRORED,
+                              /*baseIndexOffset: */m_NumIndices,
+                              numPointsForInsideEdge[parity],
+                              insideBaseOffset,outsideBaseOffset);
+            }
+            SetUsingPatchedIndices(false);
+            SetUsingPatchedIndices2(false);
+            m_NumIndices += numTriangles*3;
+            outsideEdgePointBaseOffset += numPointsForOutsideEdge[edge] - 1;
+            if( (edge == 2) && (ring == degeneratePointRing[parity]) )
+            {
+                insideEdgePointBaseOffset -= numPointsForInsideEdge[parity] - 1;
+            }
+            else
+            {
+                insideEdgePointBaseOffset += numPointsForInsideEdge[parity] - 1;
+            }
+            numPointsForOutsideEdge[edge] = numPointsForInsideEdge[parity];
+        }
+        if( startRing == ring )
+        {
+            for(edge = 0; edge < QUAD_EDGES; edge++ )
+            {
+                outsideTessFactorCtx[edge] = &processedTessFactors.insideTessFactorCtx[edge&1];
+                outsideTessFactorParity[edge] = processedTessFactors.insideTessFactorParity[edge&1];
+            }
+        }
+    }
+
+    // Triangulate center - a row of quads if odd
+    // This triangulation may be producing diagonals that are asymmetric about
+    // the center of the patch in this region.
+    if( (processedTessFactors.numPointsForInsideTessFactor[U] > processedTessFactors.numPointsForInsideTessFactor[V]) &&
+        (TESSELLATOR_PARITY_ODD == processedTessFactors.insideTessFactorParity[V] ) )
+    {
+        SetUsingPatchedIndices2(true);
+        int stripNumQuads = (((processedTessFactors.numPointsForInsideTessFactor[U]>>1) - (processedTessFactors.numPointsForInsideTessFactor[V]>>1))<<1)+
+                            ((TESSELLATOR_PARITY_EVEN == processedTessFactors.insideTessFactorParity[U] ) ? 2 : 1);
+        m_IndexPatchContext2.baseIndexToInvert = outsideEdgePointBaseOffset + stripNumQuads + 2;
+        m_IndexPatchContext2.cornerCaseBadValue = m_IndexPatchContext2.baseIndexToInvert;
+        m_IndexPatchContext2.cornerCaseReplacementValue = outsideEdgePointBaseOffset;
+        m_IndexPatchContext2.indexInversionEndPoint = m_IndexPatchContext2.baseIndexToInvert +
+                                                      m_IndexPatchContext2.baseIndexToInvert + stripNumQuads;
+        StitchRegular(/*bTrapezoid*/false,DIAGONALS_INSIDE_TO_OUTSIDE,
+                       /*baseIndexOffset: */m_NumIndices, /*numInsideEdgePoints:*/stripNumQuads+1,
+                       /*insideEdgePointBaseOffset*/m_IndexPatchContext2.baseIndexToInvert,
+                       outsideEdgePointBaseOffset+1);
+        SetUsingPatchedIndices2(false);
+        m_NumIndices += stripNumQuads*6;
+    }
+    else if((processedTessFactors.numPointsForInsideTessFactor[V] >= processedTessFactors.numPointsForInsideTessFactor[U]) &&
+            (TESSELLATOR_PARITY_ODD == processedTessFactors.insideTessFactorParity[U]) )
+    {
+        SetUsingPatchedIndices2(true);
+        int stripNumQuads = (((processedTessFactors.numPointsForInsideTessFactor[V]>>1) - (processedTessFactors.numPointsForInsideTessFactor[U]>>1))<<1)+
+                            ((TESSELLATOR_PARITY_EVEN == processedTessFactors.insideTessFactorParity[V] ) ? 2 : 1);
+        m_IndexPatchContext2.baseIndexToInvert = outsideEdgePointBaseOffset + stripNumQuads + 1;
+        m_IndexPatchContext2.cornerCaseBadValue = -1; // unused
+        m_IndexPatchContext2.indexInversionEndPoint = m_IndexPatchContext2.baseIndexToInvert +
+                                                      m_IndexPatchContext2.baseIndexToInvert + stripNumQuads;
+               DIAGONALS diag = (TESSELLATOR_PARITY_EVEN == processedTessFactors.insideTessFactorParity[V]) ?
+                                                       DIAGONALS_INSIDE_TO_OUTSIDE : DIAGONALS_INSIDE_TO_OUTSIDE_EXCEPT_MIDDLE;
+        StitchRegular(/*bTrapezoid*/false,diag,
+                       /*baseIndexOffset: */m_NumIndices, /*numInsideEdgePoints:*/stripNumQuads+1,
+                       /*insideEdgePointBaseOffset*/m_IndexPatchContext2.baseIndexToInvert,
+                       outsideEdgePointBaseOffset);
+        SetUsingPatchedIndices2(false);
+        m_NumIndices += stripNumQuads*6;
+    }
+}
+
+//---------------------------------------------------------------------------------------------------------------------------------
+// CHWTessellator::TessellateTriDomain
+// User calls this
+//---------------------------------------------------------------------------------------------------------------------------------
+void CHWTessellator::TessellateTriDomain( float tessFactor_Ueq0, float tessFactor_Veq0, float tessFactor_Weq0,
+                                        float insideTessFactor )
+{
+    PROCESSED_TESS_FACTORS_TRI processedTessFactors;
+    TriProcessTessFactors(tessFactor_Ueq0,tessFactor_Veq0,tessFactor_Weq0,insideTessFactor,processedTessFactors);
+
+    if( processedTessFactors.bPatchCulled )
+    {
+        m_NumPoints = 0;
+        m_NumIndices = 0;
+        return;
+    }
+    else if( processedTessFactors.bJustDoMinimumTessFactor )
+    {
+        DefinePoint(/*U*/0,/*V*/FXP_ONE,/*pointStorageOffset*/0); //V=1 (beginning of Ueq0 edge VW)
+        DefinePoint(/*U*/0,/*V*/0,/*pointStorageOffset*/1); //W=1 (beginning of Veq0 edge WU)
+        DefinePoint(/*U*/FXP_ONE,/*V*/0,/*pointStorageOffset*/2); //U=1 (beginning of Weq0 edge UV)
+        m_NumPoints = 3;
+
+        switch(m_outputPrimitive)
+        {
+        case D3D11_TESSELLATOR_OUTPUT_TRIANGLE_CW:
+        case D3D11_TESSELLATOR_OUTPUT_TRIANGLE_CCW:
+            // function orients them CCW if needed
+            DefineClockwiseTriangle(0,1,2,/*indexStorageBaseOffset*/m_NumIndices);
+            m_NumIndices = 3;
+            break;
+        case D3D11_TESSELLATOR_OUTPUT_POINT:
+            DumpAllPoints();
+            break;
+        case D3D11_TESSELLATOR_OUTPUT_LINE:
+            DumpAllPointsAsInOrderLineList();
+            break;
+        }
+        return;
+    }
+
+    TriGeneratePoints(processedTessFactors);
+
+    if( m_outputPrimitive == D3D11_TESSELLATOR_OUTPUT_POINT )
+    {
+        DumpAllPoints();
+        return;
+    }
+    if( m_outputPrimitive == D3D11_TESSELLATOR_OUTPUT_LINE )
+    {
+        DumpAllPointsAsInOrderLineList();
+        return;
+    }
+
+    TriGenerateConnectivity(processedTessFactors); // can be done in parallel to TriGeneratePoints()
+}
+
+//---------------------------------------------------------------------------------------------------------------------------------
+// CHWTessellator::TriProcessTessFactors
+//---------------------------------------------------------------------------------------------------------------------------------
+void CHWTessellator::TriProcessTessFactors( float tessFactor_Ueq0, float tessFactor_Veq0, float tessFactor_Weq0,
+                                            float insideTessFactor, PROCESSED_TESS_FACTORS_TRI& processedTessFactors )
+{
+    // Is the patch culled?
+    if( !(tessFactor_Ueq0 > 0) || // NaN will pass
+        !(tessFactor_Veq0 > 0) ||
+        !(tessFactor_Weq0 > 0) )
+    {
+        processedTessFactors.bPatchCulled = true;
+        return;
+    }
+    else
+    {
+        processedTessFactors.bPatchCulled = false;
+    }
+
+    // Clamp edge TessFactors
+    float lowerBound, upperBound;
+    switch(m_originalPartitioning)
+    {
+        case D3D11_TESSELLATOR_PARTITIONING_INTEGER:
+        case D3D11_TESSELLATOR_PARTITIONING_POW2: // don�t care about pow2 distinction for validation, just treat as integer
+            lowerBound = D3D11_TESSELLATOR_MIN_ODD_TESSELLATION_FACTOR;
+            upperBound = D3D11_TESSELLATOR_MAX_EVEN_TESSELLATION_FACTOR;
+            break;
+
+        case D3D11_TESSELLATOR_PARTITIONING_FRACTIONAL_EVEN:
+            lowerBound = D3D11_TESSELLATOR_MIN_EVEN_TESSELLATION_FACTOR;
+            upperBound = D3D11_TESSELLATOR_MAX_EVEN_TESSELLATION_FACTOR;
+            break;
+
+        case D3D11_TESSELLATOR_PARTITIONING_FRACTIONAL_ODD:
+            lowerBound = D3D11_TESSELLATOR_MIN_ODD_TESSELLATION_FACTOR;
+            upperBound = D3D11_TESSELLATOR_MAX_ODD_TESSELLATION_FACTOR;
+            break;
+    }
+
+    tessFactor_Ueq0 = fmin( upperBound, fmax( lowerBound, tessFactor_Ueq0 ) );
+    tessFactor_Veq0 = fmin( upperBound, fmax( lowerBound, tessFactor_Veq0 ) );
+    tessFactor_Weq0 = fmin( upperBound, fmax( lowerBound, tessFactor_Weq0 ) );
+
+    if( HWIntegerPartitioning()) // pow2 or integer, round to next int (hw doesn't care about pow2 distinction)
+    {
+        tessFactor_Ueq0 = ceil(tessFactor_Ueq0);
+        tessFactor_Veq0 = ceil(tessFactor_Veq0);
+        tessFactor_Weq0 = ceil(tessFactor_Weq0);
+    }
+
+    // Clamp inside TessFactors
+    if(D3D11_TESSELLATOR_PARTITIONING_FRACTIONAL_ODD == m_originalPartitioning)
+    {
+        if( (tessFactor_Ueq0 > MIN_ODD_TESSFACTOR_PLUS_HALF_EPSILON) ||
+            (tessFactor_Veq0 > MIN_ODD_TESSFACTOR_PLUS_HALF_EPSILON) ||
+            (tessFactor_Weq0 > MIN_ODD_TESSFACTOR_PLUS_HALF_EPSILON))
+            // Don't need the same check for insideTessFactor for tri patches,
+            // since there is only one insideTessFactor, as opposed to quad
+            // patches which have 2 insideTessFactors.
+        {
+            // Force picture frame
+            lowerBound = D3D11_TESSELLATOR_MIN_ODD_TESSELLATION_FACTOR + EPSILON;
+        }
+    }
+
+    insideTessFactor = fmin( upperBound, fmax( lowerBound, insideTessFactor ) );
+    // Note the above clamps map NaN to lowerBound
+
+    if( HWIntegerPartitioning()) // pow2 or integer, round to next int (hw doesn't care about pow2 distinction)
+    {
+        insideTessFactor = ceil(insideTessFactor);
+    }
+
+    // Reset our vertex and index buffers.  We have enough storage for the max tessFactor.
+    m_NumPoints = 0;
+    m_NumIndices = 0;
+
+    // Process tessFactors
+    float outsideTessFactor[TRI_EDGES] = {tessFactor_Ueq0, tessFactor_Veq0, tessFactor_Weq0};
+    int edge;
+    if( HWIntegerPartitioning() )
+    {
+        for( edge = 0; edge < TRI_EDGES; edge++ )
+        {
+            int edgeEven = isEven(outsideTessFactor[edge]);
+            processedTessFactors.outsideTessFactorParity[edge] = edgeEven ? TESSELLATOR_PARITY_EVEN : TESSELLATOR_PARITY_ODD;
+        }
+        processedTessFactors.insideTessFactorParity = (isEven(insideTessFactor) || (FLOAT_ONE == insideTessFactor))
+                                        ? TESSELLATOR_PARITY_EVEN : TESSELLATOR_PARITY_ODD;
+    }
+    else
+    {
+        for( edge = 0; edge < TRI_EDGES; edge++ )
+        {
+            processedTessFactors.outsideTessFactorParity[edge] = m_originalParity;
+        }
+        processedTessFactors.insideTessFactorParity = m_originalParity;
+    }
+
+    // Save fixed point TessFactors
+    for( edge = 0; edge < TRI_EDGES; edge++ )
+    {
+        processedTessFactors.outsideTessFactor[edge] = floatToFixed(outsideTessFactor[edge]);
+    }
+    processedTessFactors.insideTessFactor = floatToFixed(insideTessFactor);
+
+    if( HWIntegerPartitioning() || Odd() )
+    {
+        // Special case if all TessFactors are 1
+        if( (FXP_ONE == processedTessFactors.insideTessFactor) &&
+            (FXP_ONE == processedTessFactors.outsideTessFactor[Ueq0]) &&
+            (FXP_ONE == processedTessFactors.outsideTessFactor[Veq0]) &&
+            (FXP_ONE == processedTessFactors.outsideTessFactor[Weq0]) )
+        {
+            processedTessFactors.bJustDoMinimumTessFactor = true;
+            return;
+        }
+    }
+    processedTessFactors.bJustDoMinimumTessFactor = false;
+
+    // Compute per-TessFactor metadata
+    for(edge = 0; edge < TRI_EDGES; edge++ )
+    {
+        SetTessellationParity(processedTessFactors.outsideTessFactorParity[edge]);
+        ComputeTessFactorContext(processedTessFactors.outsideTessFactor[edge], processedTessFactors.outsideTessFactorCtx[edge]);
+    }
+    SetTessellationParity(processedTessFactors.insideTessFactorParity);
+    ComputeTessFactorContext(processedTessFactors.insideTessFactor, processedTessFactors.insideTessFactorCtx);
+
+    // Compute some initial data.
+
+    // outside edge offsets and storage
+    for(edge = 0; edge < TRI_EDGES; edge++ )
+    {
+        SetTessellationParity(processedTessFactors.outsideTessFactorParity[edge]);
+        processedTessFactors.numPointsForOutsideEdge[edge] = NumPointsForTessFactor(processedTessFactors.outsideTessFactor[edge]);
+        m_NumPoints += processedTessFactors.numPointsForOutsideEdge[edge];
+    }
+    m_NumPoints -= 3;
+
+    // inside edge offsets
+    SetTessellationParity(processedTessFactors.insideTessFactorParity);
+    processedTessFactors.numPointsForInsideTessFactor = NumPointsForTessFactor(processedTessFactors.insideTessFactor);
+    {
+        int pointCountMin = Odd() ? 4 : 3;
+        // max() allows degenerate transition regions when inside TessFactor == 1
+        processedTessFactors.numPointsForInsideTessFactor = max(pointCountMin,processedTessFactors.numPointsForInsideTessFactor);
+    }
+
+    processedTessFactors.insideEdgePointBaseOffset = m_NumPoints;
+
+    // inside storage, including interior edges above
+    {
+        int numInteriorRings = (processedTessFactors.numPointsForInsideTessFactor >> 1) - 1;
+        int numInteriorPoints;
+        if( Odd() )
+        {
+            numInteriorPoints = TRI_EDGES*(numInteriorRings*(numInteriorRings+1) - numInteriorRings);
+        }
+        else
+        {
+            numInteriorPoints = TRI_EDGES*(numInteriorRings*(numInteriorRings+1)) + 1;
+        }
+        m_NumPoints += numInteriorPoints;
+    }
+
+}
+
+//---------------------------------------------------------------------------------------------------------------------------------
+// CHWTessellator::TriGeneratePoints
+//---------------------------------------------------------------------------------------------------------------------------------
+void CHWTessellator::TriGeneratePoints( const PROCESSED_TESS_FACTORS_TRI& processedTessFactors )
+{
+    // Generate exterior ring edge points, clockwise starting from point V (VW, the U==0 edge)
+    int pointOffset = 0;
+    int edge;
+    for(edge = 0; edge < TRI_EDGES; edge++ )
+    {
+        int parity = edge&0x1;
+        int startPoint = 0;
+        int endPoint = processedTessFactors.numPointsForOutsideEdge[edge] - 1;
+        for(int p = startPoint; p < endPoint; p++, pointOffset++) // don't include end, since next edge starts with it.
+        {
+            FXP fxpParam;
+            int q = (parity) ? p : endPoint - p; // whether to reverse point order given we are defining V or U (W implicit):
+                                                 // edge0, VW, has V decreasing, so reverse 1D points below
+                                                 // edge1, WU, has U increasing, so don't reverse 1D points  below
+                                                 // edge2, UV, has U decreasing, so reverse 1D points below
+            SetTessellationParity(processedTessFactors.outsideTessFactorParity[edge]);
+            PlacePointIn1D(processedTessFactors.outsideTessFactorCtx[edge],q,fxpParam);
+            if( edge == 0 )
+            {
+                DefinePoint(/*U*/0,
+                            /*V*/fxpParam,
+                            /*pointStorageOffset*/pointOffset);
+            }
+            else
+            {
+                DefinePoint(/*U*/fxpParam,
+                            /*V*/(edge == 2) ? FXP_ONE - fxpParam : 0,
+                            /*pointStorageOffset*/pointOffset);
+            }
+        }
+    }
+
+    // Generate interior ring points, clockwise spiralling in
+    SetTessellationParity(processedTessFactors.insideTessFactorParity);
+    static const int startRing = 1;
+    int numRings = (processedTessFactors.numPointsForInsideTessFactor >> 1);
+    for(int ring = startRing; ring < numRings; ring++)
+    {
+        int startPoint = ring;
+        int endPoint = processedTessFactors.numPointsForInsideTessFactor - 1 - startPoint;
+
+        for(edge = 0; edge < TRI_EDGES; edge++ )
+        {
+            int parity = edge&0x1;
+            int perpendicularAxisPoint = startPoint;
+            FXP fxpPerpParam;
+            PlacePointIn1D(processedTessFactors.insideTessFactorCtx,perpendicularAxisPoint,fxpPerpParam);
+            fxpPerpParam *= FXP_TWO_THIRDS; // Map location to the right size in barycentric space.
+                                         // I (amarp) can draw a picture to explain.
+                                         // We know this fixed point math won't over/underflow
+            fxpPerpParam = (fxpPerpParam+FXP_ONE_HALF/*round*/)>>FXP_FRACTION_BITS; // get back to n.16
+            for(int p = startPoint; p < endPoint; p++, pointOffset++) // don't include end: next edge starts with it.
+            {
+                FXP fxpParam;
+                int q = (parity) ? p : endPoint - (p - startPoint); // whether to reverse point given we are defining V or U (W implicit):
+                                                         // edge0, VW, has V decreasing, so reverse 1D points below
+                                                         // edge1, WU, has U increasing, so don't reverse 1D points  below
+                                                         // edge2, UV, has U decreasing, so reverse 1D points below
+                PlacePointIn1D(processedTessFactors.insideTessFactorCtx,q,fxpParam);
+                // edge0 VW, has perpendicular parameter U constant
+                // edge1 WU, has perpendicular parameter V constant
+                // edge2 UV, has perpendicular parameter W constant
+                const unsigned int deriv = 2; // reciprocal is the rate of change of edge-parallel parameters as they are pushed into the triangle
+                switch(edge)
+                {
+                case 0:
+                    DefinePoint(/*U*/fxpPerpParam,
+                                /*V*/fxpParam - (fxpPerpParam+1/*round*/)/deriv, // we know this fixed point math won't over/underflow
+                                /*pointStorageOffset*/pointOffset);
+                    break;
+                case 1:
+                    DefinePoint(/*U*/fxpParam - (fxpPerpParam+1/*round*/)/deriv,// we know this fixed point math won't over/underflow
+                                /*V*/fxpPerpParam,
+                                /*pointStorageOffset*/pointOffset);
+                    break;
+                case 2:
+                    DefinePoint(/*U*/fxpParam - (fxpPerpParam+1/*round*/)/deriv,// we know this fixed point math won't over/underflow
+                                /*V*/FXP_ONE - (fxpParam - (fxpPerpParam+1/*round*/)/deriv) - fxpPerpParam,// we know this fixed point math won't over/underflow
+                                /*pointStorageOffset*/pointOffset);
+                    break;
+                }
+            }
+        }
+    }
+    if( !Odd() )
+    {
+        // Last point is the point at the center.
+        DefinePoint(/*U*/FXP_ONE_THIRD,
+                    /*V*/FXP_ONE_THIRD,
+                    /*pointStorageOffset*/pointOffset);
+    }
+}
+//---------------------------------------------------------------------------------------------------------------------------------
+// CHWTessellator::TriGenerateConnectivity
+//---------------------------------------------------------------------------------------------------------------------------------
+void CHWTessellator::TriGenerateConnectivity( const PROCESSED_TESS_FACTORS_TRI& processedTessFactors )
+{
+    // Generate primitives for all the concentric rings, one side at a time for each ring
+    static const int startRing = 1;
+    int numRings = ((processedTessFactors.numPointsForInsideTessFactor+1) >> 1); // +1 is so even tess includes the center point, which we want to now
+    const TESS_FACTOR_CONTEXT* outsideTessFactorCtx[TRI_EDGES] = {&processedTessFactors.outsideTessFactorCtx[Ueq0],
+                                            &processedTessFactors.outsideTessFactorCtx[Veq0],
+                                            &processedTessFactors.outsideTessFactorCtx[Weq0]};
+    TESSELLATOR_PARITY outsideTessFactorParity[TRI_EDGES] = {processedTessFactors.outsideTessFactorParity[Ueq0],
+                                            processedTessFactors.outsideTessFactorParity[Veq0],
+                                            processedTessFactors.outsideTessFactorParity[Weq0]};
+    int numPointsForOutsideEdge[TRI_EDGES] = {processedTessFactors.numPointsForOutsideEdge[Ueq0],
+                                              processedTessFactors.numPointsForOutsideEdge[Veq0],
+                                              processedTessFactors.numPointsForOutsideEdge[Weq0]};
+
+    int insideEdgePointBaseOffset = processedTessFactors.insideEdgePointBaseOffset;
+    int outsideEdgePointBaseOffset = 0;
+    int edge;
+    for(int ring = startRing; ring < numRings; ring++)
+    {
+        int numPointsForInsideEdge = processedTessFactors.numPointsForInsideTessFactor - 2*ring;
+        int edge0InsidePointBaseOffset = insideEdgePointBaseOffset;
+        int edge0OutsidePointBaseOffset = outsideEdgePointBaseOffset;
+        for(edge = 0; edge < TRI_EDGES; edge++ )
+        {
+            int numTriangles = numPointsForInsideEdge + numPointsForOutsideEdge[edge] - 2;
+
+            int insideBaseOffset;
+            int outsideBaseOffset;
+            if( edge == 2 )
+            {
+                m_IndexPatchContext.insidePointIndexDeltaToRealValue    = insideEdgePointBaseOffset;
+                m_IndexPatchContext.insidePointIndexBadValue            = numPointsForInsideEdge - 1;
+                m_IndexPatchContext.insidePointIndexReplacementValue    = edge0InsidePointBaseOffset;
+                m_IndexPatchContext.outsidePointIndexPatchBase          = m_IndexPatchContext.insidePointIndexBadValue+1; // past inside patched index range
+                m_IndexPatchContext.outsidePointIndexDeltaToRealValue   = outsideEdgePointBaseOffset
+                                                                            - m_IndexPatchContext.outsidePointIndexPatchBase;
+                m_IndexPatchContext.outsidePointIndexBadValue           = m_IndexPatchContext.outsidePointIndexPatchBase
+                                                                            + numPointsForOutsideEdge[edge] - 1;
+                m_IndexPatchContext.outsidePointIndexReplacementValue   = edge0OutsidePointBaseOffset;
+                SetUsingPatchedIndices(true);
+                insideBaseOffset = 0;
+                outsideBaseOffset = m_IndexPatchContext.outsidePointIndexPatchBase;
+            }
+            else
+            {
+                insideBaseOffset = insideEdgePointBaseOffset;
+                outsideBaseOffset = outsideEdgePointBaseOffset;
+            }
+            if( ring == startRing )
+            {
+                StitchTransition(/*baseIndexOffset: */m_NumIndices,
+                               insideBaseOffset,processedTessFactors.insideTessFactorCtx.numHalfTessFactorPoints,processedTessFactors.insideTessFactorParity,
+                               outsideBaseOffset,outsideTessFactorCtx[edge]->numHalfTessFactorPoints,outsideTessFactorParity[edge]);
+            }
+            else
+            {
+                StitchRegular(/*bTrapezoid*/true, DIAGONALS_MIRRORED,
+                              /*baseIndexOffset: */m_NumIndices,
+                              numPointsForInsideEdge,
+                              insideBaseOffset,outsideBaseOffset);
+            }
+            if( 2 == edge )
+            {
+                SetUsingPatchedIndices(false);
+            }
+            m_NumIndices += numTriangles*3;
+            outsideEdgePointBaseOffset += numPointsForOutsideEdge[edge] - 1;
+            insideEdgePointBaseOffset += numPointsForInsideEdge - 1;
+            numPointsForOutsideEdge[edge] = numPointsForInsideEdge;
+        }
+        if( startRing == ring )
+        {
+            for(edge = 0; edge < TRI_EDGES; edge++ )
+            {
+                outsideTessFactorCtx[edge] = &processedTessFactors.insideTessFactorCtx;
+                outsideTessFactorParity[edge] = processedTessFactors.insideTessFactorParity;
+            }
+        }
+    }
+    if( Odd() )
+    {
+        // Triangulate center (a single triangle)
+        DefineClockwiseTriangle(outsideEdgePointBaseOffset, outsideEdgePointBaseOffset+1, outsideEdgePointBaseOffset+2,
+                       m_NumIndices);
+        m_NumIndices += 3;
+    }
+}
+
+//---------------------------------------------------------------------------------------------------------------------------------
+// CHWTessellator::TessellateIsoLineDomain
+// User calls this.
+//---------------------------------------------------------------------------------------------------------------------------------
+void CHWTessellator::TessellateIsoLineDomain( float TessFactor_V_LineDensity, float TessFactor_U_LineDetail )
+{
+    PROCESSED_TESS_FACTORS_ISOLINE processedTessFactors;
+    IsoLineProcessTessFactors(TessFactor_V_LineDensity,TessFactor_U_LineDetail,processedTessFactors);
+    if( processedTessFactors.bPatchCulled )
+    {
+        m_NumPoints = 0;
+        m_NumIndices = 0;
+        return;
+    }
+    IsoLineGeneratePoints(processedTessFactors);
+    IsoLineGenerateConnectivity(processedTessFactors); // can be done in parallel to IsoLineGeneratePoints
+}
+
+//---------------------------------------------------------------------------------------------------------------------------------
+// CHWTessellator::IsoLineProcessTessFactors
+//---------------------------------------------------------------------------------------------------------------------------------
+void CHWTessellator::IsoLineProcessTessFactors( float TessFactor_V_LineDensity, float TessFactor_U_LineDetail,
+                                                PROCESSED_TESS_FACTORS_ISOLINE& processedTessFactors )
+{
+    // Is the patch culled?
+    if( !(TessFactor_V_LineDensity > 0) || // NaN will pass
+        !(TessFactor_U_LineDetail > 0) )
+    {
+        processedTessFactors.bPatchCulled = true;
+        return;
+    }
+    else
+    {
+        processedTessFactors.bPatchCulled = false;
+    }
+
+    // Clamp edge TessFactors
+    float lowerBound, upperBound;
+    switch(m_originalPartitioning)
+    {
+        case D3D11_TESSELLATOR_PARTITIONING_INTEGER:
+        case D3D11_TESSELLATOR_PARTITIONING_POW2: // don�t care about pow2 distinction for validation, just treat as integer
+            lowerBound = D3D11_TESSELLATOR_MIN_ODD_TESSELLATION_FACTOR;
+            upperBound = D3D11_TESSELLATOR_MAX_EVEN_TESSELLATION_FACTOR;
+            break;
+
+        case D3D11_TESSELLATOR_PARTITIONING_FRACTIONAL_EVEN:
+            lowerBound = D3D11_TESSELLATOR_MIN_EVEN_TESSELLATION_FACTOR;
+            upperBound = D3D11_TESSELLATOR_MAX_EVEN_TESSELLATION_FACTOR;
+            break;
+
+        case D3D11_TESSELLATOR_PARTITIONING_FRACTIONAL_ODD:
+            lowerBound = D3D11_TESSELLATOR_MIN_ODD_TESSELLATION_FACTOR;
+            upperBound = D3D11_TESSELLATOR_MAX_ODD_TESSELLATION_FACTOR;
+            break;
+    }
+
+    TessFactor_V_LineDensity = fmin( D3D11_TESSELLATOR_MAX_ISOLINE_DENSITY_TESSELLATION_FACTOR,
+                                    fmax( D3D11_TESSELLATOR_MIN_ISOLINE_DENSITY_TESSELLATION_FACTOR, TessFactor_V_LineDensity ) );
+    TessFactor_U_LineDetail = fmin( upperBound, fmax( lowerBound, TessFactor_U_LineDetail ) );
+
+    // Reset our vertex and index buffers.  We have enough storage for the max tessFactor.
+    m_NumPoints = 0;
+    m_NumIndices = 0;
+
+    // Process tessFactors
+    if( HWIntegerPartitioning() )
+    {
+        TessFactor_U_LineDetail = ceil(TessFactor_U_LineDetail);
+        processedTessFactors.lineDetailParity = isEven(TessFactor_U_LineDetail) ? TESSELLATOR_PARITY_EVEN : TESSELLATOR_PARITY_ODD;
+    }
+    else
+    {
+        processedTessFactors.lineDetailParity = m_originalParity;
+    }
+
+    FXP fxpTessFactor_U_LineDetail = floatToFixed(TessFactor_U_LineDetail);
+
+    SetTessellationParity(processedTessFactors.lineDetailParity);
+
+    ComputeTessFactorContext(fxpTessFactor_U_LineDetail, processedTessFactors.lineDetailTessFactorCtx);
+    processedTessFactors.numPointsPerLine = NumPointsForTessFactor(fxpTessFactor_U_LineDetail);
+
+    OverridePartitioning(D3D11_TESSELLATOR_PARTITIONING_INTEGER);
+
+    TessFactor_V_LineDensity = ceil(TessFactor_V_LineDensity);
+    processedTessFactors.lineDensityParity = isEven(TessFactor_V_LineDensity) ? TESSELLATOR_PARITY_EVEN : TESSELLATOR_PARITY_ODD;
+    SetTessellationParity(processedTessFactors.lineDensityParity);
+    FXP fxpTessFactor_V_LineDensity = floatToFixed(TessFactor_V_LineDensity);
+    ComputeTessFactorContext(fxpTessFactor_V_LineDensity, processedTessFactors.lineDensityTessFactorCtx);
+
+    processedTessFactors.numLines = NumPointsForTessFactor(fxpTessFactor_V_LineDensity) - 1; // don't draw last line at V == 1.
+
+    RestorePartitioning();
+
+    // Compute some initial data.
+
+    // outside edge offsets
+    m_NumPoints = processedTessFactors.numPointsPerLine * processedTessFactors.numLines;
+    if( m_outputPrimitive == D3D11_TESSELLATOR_OUTPUT_POINT )
+    {
+        m_NumIndices = m_NumPoints;
+    }
+    else // line
+    {
+        m_NumIndices = processedTessFactors.numLines*(processedTessFactors.numPointsPerLine-1)*2;
+    }
+}
+
+//---------------------------------------------------------------------------------------------------------------------------------
+// CHWTessellator::IsoLineGeneratePoints
+//---------------------------------------------------------------------------------------------------------------------------------
+void CHWTessellator::IsoLineGeneratePoints( const PROCESSED_TESS_FACTORS_ISOLINE& processedTessFactors )
+{
+    int line, pointOffset;
+    for(line = 0, pointOffset = 0; line < processedTessFactors.numLines; line++)
+    {
+        for(int point = 0; point < processedTessFactors.numPointsPerLine; point++)
+        {
+            FXP fxpU,fxpV;
+            SetTessellationParity(processedTessFactors.lineDensityParity);
+            PlacePointIn1D(processedTessFactors.lineDensityTessFactorCtx,line,fxpV);
+
+            SetTessellationParity(processedTessFactors.lineDetailParity);
+            PlacePointIn1D(processedTessFactors.lineDetailTessFactorCtx,point,fxpU);
+
+            DefinePoint(fxpU,fxpV,pointOffset++);
+        }
+    }
+}
+
+//---------------------------------------------------------------------------------------------------------------------------------
+// CHWTessellator::IsoLineGenerateConnectivity
+//---------------------------------------------------------------------------------------------------------------------------------
+void CHWTessellator::IsoLineGenerateConnectivity( const PROCESSED_TESS_FACTORS_ISOLINE& processedTessFactors )
+{
+    int line, pointOffset, indexOffset;
+    if( m_outputPrimitive == D3D11_TESSELLATOR_OUTPUT_POINT )
+    {
+        for(line = 0, pointOffset = 0, indexOffset = 0; line < processedTessFactors.numLines; line++)
+        {
+            for(int point = 0; point < processedTessFactors.numPointsPerLine; point++)
+            {
+                DefineIndex(pointOffset++,indexOffset++);
+            }
+        }
+    }
+    else // line
+    {
+        for(line = 0, pointOffset = 0, indexOffset = 0; line < processedTessFactors.numLines; line++)
+        {
+            for(int point = 0; point < processedTessFactors.numPointsPerLine; point++)
+            {
+                if( point > 0 )
+                {
+                    DefineIndex(pointOffset-1,indexOffset++);
+                    DefineIndex(pointOffset,indexOffset++);
+                }
+                pointOffset++;
+            }
+        }
+    }
+}
+
+//---------------------------------------------------------------------------------------------------------------------------------
+// CHWTessellator::GetPointCount
+// User calls this.
+//---------------------------------------------------------------------------------------------------------------------------------
+int CHWTessellator::GetPointCount()
+{
+    return m_NumPoints;
+}
+
+//---------------------------------------------------------------------------------------------------------------------------------
+// CHWTessellator::GetIndexCount()
+// User calls this.
+//---------------------------------------------------------------------------------------------------------------------------------
+int CHWTessellator::GetIndexCount()
+{
+    return m_NumIndices;
+}
+
+//---------------------------------------------------------------------------------------------------------------------------------
+// CHWTessellator::GetPoints()
+// User calls this.
+//---------------------------------------------------------------------------------------------------------------------------------
+DOMAIN_POINT* CHWTessellator::GetPoints()
+{
+    return m_Point;
+}
+//---------------------------------------------------------------------------------------------------------------------------------
+// CHWTessellator::GetIndices()
+// User calls this.
+//---------------------------------------------------------------------------------------------------------------------------------
+int* CHWTessellator::GetIndices()
+{
+    return m_Index;
+}
+
+//---------------------------------------------------------------------------------------------------------------------------------
+// CHWTessellator::DefinePoint()
+//---------------------------------------------------------------------------------------------------------------------------------
+int CHWTessellator::DefinePoint(FXP fxpU, FXP fxpV, int pointStorageOffset)
+{
+//    WCHAR foo[80];
+//    StringCchPrintf(foo,80,L"off:%d, uv=(%f,%f)\n",pointStorageOffset,fixedToFloat(fxpU),fixedToFloat(fxpV));
+//    OutputDebugString(foo);
+    m_Point[pointStorageOffset].u = fixedToFloat(fxpU);
+    m_Point[pointStorageOffset].v = fixedToFloat(fxpV);
+    return pointStorageOffset;
+}
+
+//---------------------------------------------------------------------------------------------------------------------------------
+// CHWTessellator::DefineIndex()
+//--------------------------------------------------------------------------------------------------------------------------------
+void CHWTessellator::DefineIndex(int index, int indexStorageOffset)
+{
+    index = PatchIndexValue(index);
+//    WCHAR foo[80];
+//    StringCchPrintf(foo,80,L"off:%d, idx=%d, uv=(%f,%f)\n",indexStorageOffset,index,m_Point[index].u,m_Point[index].v);
+//    OutputDebugString(foo);
+    m_Index[indexStorageOffset] = index;
+}
+
+//---------------------------------------------------------------------------------------------------------------------------------
+// CHWTessellator::DefineClockwiseTriangle()
+//---------------------------------------------------------------------------------------------------------------------------------
+void CHWTessellator::DefineClockwiseTriangle(int index0, int index1, int index2, int indexStorageBaseOffset)
+{
+    // inputs a clockwise triangle, stores a CW or CCW triangle depending on the state
+    DefineIndex(index0,indexStorageBaseOffset);
+    bool bWantClockwise = (m_outputPrimitive == D3D11_TESSELLATOR_OUTPUT_TRIANGLE_CW) ? true : false;
+    if( bWantClockwise )
+    {
+        DefineIndex(index1,indexStorageBaseOffset+1);
+        DefineIndex(index2,indexStorageBaseOffset+2);
+    }
+    else
+    {
+        DefineIndex(index2,indexStorageBaseOffset+1);
+        DefineIndex(index1,indexStorageBaseOffset+2);
+    }
+}
+
+//---------------------------------------------------------------------------------------------------------------------------------
+// CHWTessellator::DumpAllPoints()
+//---------------------------------------------------------------------------------------------------------------------------------
+void CHWTessellator::DumpAllPoints()
+{
+    for( int p = 0; p < m_NumPoints; p++ )
+    {
+        DefineIndex(p,m_NumIndices++);
+    }
+}
+
+//---------------------------------------------------------------------------------------------------------------------------------
+// CHWTessellator::DumpAllPointsAsInOrderLineList()
+//---------------------------------------------------------------------------------------------------------------------------------
+void CHWTessellator::DumpAllPointsAsInOrderLineList()
+{
+    for( int p = 1; p < m_NumPoints; p++ )
+    {
+        DefineIndex(p-1,m_NumIndices++);
+        DefineIndex(p,m_NumIndices++);
+    }
+}
+
+//---------------------------------------------------------------------------------------------------------------------------------
+// RemoveMSB
+//---------------------------------------------------------------------------------------------------------------------------------
+int RemoveMSB(int val)
+{
+    int check;
+    if( val <= 0x0000ffff ) { check = ( val <= 0x000000ff ) ? 0x00000080 : 0x00008000; }
+    else                    { check = ( val <= 0x00ffffff ) ? 0x00800000 : 0x80000000; }
+    for( int i = 0; i < 8; i++, check >>= 1 ) { if( val & check ) return (val & ~check); }
+    return 0;
+}
+//---------------------------------------------------------------------------------------------------------------------------------
+// GetMSB
+//---------------------------------------------------------------------------------------------------------------------------------
+int GetMSB(int val)
+{
+    int check;
+    if( val <= 0x0000ffff ) { check = ( val <= 0x000000ff ) ? 0x00000080 : 0x00008000; }
+    else                    { check = ( val <= 0x00ffffff ) ? 0x00800000 : 0x80000000; }
+    for( int i = 0; i < 8; i++, check >>= 1 ) { if( val & check ) return check; }
+    return 0;
+}
+
+//---------------------------------------------------------------------------------------------------------------------------------
+// CHWTessellator::CleanseParameter()
+//---------------------------------------------------------------------------------------------------------------------------------
+/* NOTHING TO DO FOR FIXED POINT ARITHMETIC!
+void CHWTessellator::CleanseParameter(float& parameter)
+{
+    // Clean up [0..1] parameter to guarantee that (1 - (1 - parameter)) == parameter.
+    parameter = 1.0f - parameter;
+    parameter = 1.0f - parameter;
+
+}
+*/
+//---------------------------------------------------------------------------------------------------------------------------------
+// CHWTessellator::NumPointsForTessFactor()
+//---------------------------------------------------------------------------------------------------------------------------------
+int CHWTessellator::NumPointsForTessFactor( FXP fxpTessFactor )
+{
+    int numPoints;
+    if( Odd() )
+    {
+        numPoints = (fxpCeil(FXP_ONE_HALF + (fxpTessFactor+1/*round*/)/2)*2)>>FXP_FRACTION_BITS;
+    }
+    else
+    {
+        numPoints = ((fxpCeil((fxpTessFactor+1/*round*/)/2)*2)>>FXP_FRACTION_BITS)+1;
+    }
+    return numPoints;
+}
+
+//---------------------------------------------------------------------------------------------------------------------------------
+// CHWTessellator::ComputeTessFactorContext()
+//---------------------------------------------------------------------------------------------------------------------------------
+void CHWTessellator::ComputeTessFactorContext( FXP fxpTessFactor, TESS_FACTOR_CONTEXT& TessFactorCtx )
+{
+    FXP fxpHalfTessFactor = (fxpTessFactor+1/*round*/)/2;
+    if( Odd() || (fxpHalfTessFactor == FXP_ONE_HALF)) // fxpHalfTessFactor == 1/2 if TessFactor is 1, but we're pretending we are even.
+    {
+        fxpHalfTessFactor += FXP_ONE_HALF;
+    }
+    FXP fxpFloorHalfTessFactor = fxpFloor(fxpHalfTessFactor);
+    FXP fxpCeilHalfTessFactor = fxpCeil(fxpHalfTessFactor);
+    TessFactorCtx.fxpHalfTessFactorFraction = fxpHalfTessFactor - fxpFloorHalfTessFactor;
+    //CleanseParameter(TessFactorCtx.fxpHalfTessFactorFraction);
+    TessFactorCtx.numHalfTessFactorPoints = (fxpCeilHalfTessFactor>>FXP_FRACTION_BITS); // for EVEN, we don't include the point always fixed at the midpoint of the TessFactor
+    if( fxpCeilHalfTessFactor == fxpFloorHalfTessFactor )
+    {
+        TessFactorCtx.splitPointOnFloorHalfTessFactor =  /*pick value to cause this to be ignored*/ TessFactorCtx.numHalfTessFactorPoints+1;
+    }
+    else if( Odd() )
+    {
+        if( fxpFloorHalfTessFactor == FXP_ONE )
+        {
+            TessFactorCtx.splitPointOnFloorHalfTessFactor = 0;
+        }
+        else
+        {
+#ifdef ALLOW_XBOX_360_COMPARISON
+            if( m_bXBox360Mode )
+                TessFactorCtx.splitPointOnFloorHalfTessFactor = TessFactorCtx.numHalfTessFactorPoints-2;
+            else
+#endif
+                               TessFactorCtx.splitPointOnFloorHalfTessFactor = (RemoveMSB((fxpFloorHalfTessFactor>>FXP_FRACTION_BITS)-1)<<1) + 1;
+        }
+    }
+    else
+    {
+#ifdef ALLOW_XBOX_360_COMPARISON
+        if( m_bXBox360Mode )
+            TessFactorCtx.splitPointOnFloorHalfTessFactor = TessFactorCtx.numHalfTessFactorPoints-1;
+        else
+#endif
+                       TessFactorCtx.splitPointOnFloorHalfTessFactor = (RemoveMSB(fxpFloorHalfTessFactor>>FXP_FRACTION_BITS)<<1) + 1;
+    }
+    int numFloorSegments = (fxpFloorHalfTessFactor * 2)>>FXP_FRACTION_BITS;
+    int numCeilSegments = (fxpCeilHalfTessFactor * 2)>>FXP_FRACTION_BITS;
+    if( Odd() )
+    {
+        numFloorSegments -= 1;
+        numCeilSegments -= 1;
+    }
+    TessFactorCtx.fxpInvNumSegmentsOnFloorTessFactor = s_fixedReciprocal[numFloorSegments];
+    TessFactorCtx.fxpInvNumSegmentsOnCeilTessFactor = s_fixedReciprocal[numCeilSegments];
+}
+
+//---------------------------------------------------------------------------------------------------------------------------------
+// CHWTessellator::PlacePointIn1D()
+//---------------------------------------------------------------------------------------------------------------------------------
+void CHWTessellator::PlacePointIn1D( const TESS_FACTOR_CONTEXT& TessFactorCtx, int point, FXP& fxpLocation )
+{
+    bool bFlip;
+    if( point >= TessFactorCtx.numHalfTessFactorPoints )
+    {
+        point = (TessFactorCtx.numHalfTessFactorPoints << 1) - point;
+        if( Odd() )
+        {
+            point -= 1;
+        }
+        bFlip = true;
+    }
+    else
+    {
+        bFlip = false;
+    }
+    if( point == TessFactorCtx.numHalfTessFactorPoints )
+    {
+        fxpLocation = FXP_ONE_HALF; // special casing middle since 16 bit fixed math below can't reproduce 0.5 exactly
+        return;
+    }
+    unsigned int indexOnCeilHalfTessFactor = point;
+    unsigned int indexOnFloorHalfTessFactor = indexOnCeilHalfTessFactor;
+    if( point > TessFactorCtx.splitPointOnFloorHalfTessFactor )
+    {
+        indexOnFloorHalfTessFactor -= 1;
+    }
+    // For the fixed point multiplies below, we know the results are <= 16 bits because
+    // the locations on the halfTessFactor are <= half the number of segments for the total TessFactor.
+    // So a number divided by a number that is at least twice as big will give
+    // a result no bigger than 0.5 (which in fixed point is 16 bits in our case)
+    FXP fxpLocationOnFloorHalfTessFactor = indexOnFloorHalfTessFactor * TessFactorCtx.fxpInvNumSegmentsOnFloorTessFactor;
+    FXP fxpLocationOnCeilHalfTessFactor = indexOnCeilHalfTessFactor * TessFactorCtx.fxpInvNumSegmentsOnCeilTessFactor;
+
+    // Since we know the numbers calculated above are <= fixed point 0.5, and the equation
+    // below is just lerping between two values <= fixed point 0.5 (0x00008000), then we know
+    // that the final result before shifting by 16 bits is no larger than 0x80000000.  Once we
+    // shift that down by 16, we get the result of lerping 2 numbers <= 0.5, which is obviously
+    // at most 0.5 (0x00008000)
+    fxpLocation = fxpLocationOnFloorHalfTessFactor * (FXP_ONE - TessFactorCtx.fxpHalfTessFactorFraction) +
+                  fxpLocationOnCeilHalfTessFactor * (TessFactorCtx.fxpHalfTessFactorFraction);
+    fxpLocation = (fxpLocation + FXP_ONE_HALF/*round*/) >> FXP_FRACTION_BITS; // get back to n.16
+    /* Commenting out floating point version.  Note the parameter cleansing it does is not needed in fixed point.
+    if( bFlip )
+        location = 1.0f - location; // complement produces cleansed result.
+    else
+        CleanseParameter(location);
+    */
+    if( bFlip )
+    {
+        fxpLocation = FXP_ONE - fxpLocation;
+    }
+}
+
+//---------------------------------------------------------------------------------------------------------------------------------
+// CHWTessellator::StitchRegular
+//---------------------------------------------------------------------------------------------------------------------------------
+void CHWTessellator::StitchRegular(bool bTrapezoid,DIAGONALS diagonals,
+                                 int baseIndexOffset, int numInsideEdgePoints,
+                                 int insideEdgePointBaseOffset, int outsideEdgePointBaseOffset)
+{
+    int insidePoint = insideEdgePointBaseOffset;
+    int outsidePoint = outsideEdgePointBaseOffset;
+    if( bTrapezoid )
+    {
+        DefineClockwiseTriangle(outsidePoint,outsidePoint+1,insidePoint,baseIndexOffset);
+        baseIndexOffset += 3; outsidePoint++;
+    }
+    int p;
+    switch( diagonals )
+    {
+    case DIAGONALS_INSIDE_TO_OUTSIDE:
+        // Diagonals pointing from inside edge forward towards outside edge
+        for( p = 0; p < numInsideEdgePoints-1; p++ )
+        {
+            DefineClockwiseTriangle(insidePoint,outsidePoint,outsidePoint+1,baseIndexOffset);
+            baseIndexOffset += 3;
+
+            DefineClockwiseTriangle(insidePoint,outsidePoint+1,insidePoint+1,baseIndexOffset);
+            baseIndexOffset += 3;
+            insidePoint++; outsidePoint++;
+        }
+        break;
+    case DIAGONALS_INSIDE_TO_OUTSIDE_EXCEPT_MIDDLE: // Assumes ODD tessellation
+        // Diagonals pointing from outside edge forward towards inside edge
+
+        // First half
+        for( p = 0; p < numInsideEdgePoints/2-1; p++ )
+        {
+            DefineClockwiseTriangle(outsidePoint,outsidePoint+1,insidePoint,baseIndexOffset);
+            baseIndexOffset += 3;
+            DefineClockwiseTriangle(insidePoint,outsidePoint+1,insidePoint+1,baseIndexOffset);
+            baseIndexOffset += 3;
+            insidePoint++; outsidePoint++;
+        }
+
+        // Middle
+        DefineClockwiseTriangle(outsidePoint,insidePoint+1,insidePoint,baseIndexOffset);
+        baseIndexOffset += 3;
+        DefineClockwiseTriangle(outsidePoint,outsidePoint+1,insidePoint+1,baseIndexOffset);
+        baseIndexOffset += 3;
+        insidePoint++; outsidePoint++; p+=2;
+
+        // Second half
+        for( ; p < numInsideEdgePoints; p++ )
+        {
+            DefineClockwiseTriangle(outsidePoint,outsidePoint+1,insidePoint,baseIndexOffset);
+            baseIndexOffset += 3;
+            DefineClockwiseTriangle(insidePoint,outsidePoint+1,insidePoint+1,baseIndexOffset);
+            baseIndexOffset += 3;
+            insidePoint++; outsidePoint++;
+        }
+        break;
+    case DIAGONALS_MIRRORED:
+        // First half, diagonals pointing from outside of outside edge to inside of inside edge
+        for( p = 0; p < numInsideEdgePoints/2; p++ )
+        {
+            DefineClockwiseTriangle(outsidePoint,insidePoint+1,insidePoint,baseIndexOffset);
+            baseIndexOffset += 3;
+            DefineClockwiseTriangle(outsidePoint,outsidePoint+1,insidePoint+1,baseIndexOffset);
+            baseIndexOffset += 3;
+            insidePoint++; outsidePoint++;
+        }
+        // Second half, diagonals pointing from inside of inside edge to outside of outside edge
+        for( ; p < numInsideEdgePoints-1; p++ )
+        {
+            DefineClockwiseTriangle(insidePoint,outsidePoint,outsidePoint+1,baseIndexOffset);
+            baseIndexOffset += 3;
+            DefineClockwiseTriangle(insidePoint,outsidePoint+1,insidePoint+1,baseIndexOffset);
+            baseIndexOffset += 3;
+            insidePoint++; outsidePoint++;
+        }
+        break;
+    }
+    if( bTrapezoid )
+    {
+        DefineClockwiseTriangle(outsidePoint,outsidePoint+1,insidePoint,baseIndexOffset);
+        baseIndexOffset += 3;
+    }
+}
+
+//---------------------------------------------------------------------------------------------------------------------------------
+// CHWTessellator::StitchTransition()
+//---------------------------------------------------------------------------------------------------------------------------------
+void CHWTessellator::StitchTransition(int baseIndexOffset,
+                                    int insideEdgePointBaseOffset, int insideNumHalfTessFactorPoints,
+                                    TESSELLATOR_PARITY insideEdgeTessFactorParity,
+                                    int outsideEdgePointBaseOffset, int outsideNumHalfTessFactorPoints,
+                                    TESSELLATOR_PARITY outsideTessFactorParity
+)
+{
+
+#ifdef ALLOW_XBOX_360_COMPARISON
+    // Tables to assist in the stitching of 2 rows of points having arbitrary TessFactors.
+    // The stitching order is governed by Ruler Function vertex split ordering (see external documentation).
+    //
+    // The contents of the finalPointPositionTable are where vertex i [0..32] ends up on the half-edge
+    // at the max tessellation amount given ruler-function split order.
+    // Recall the other half of an edge is mirrored, so we only need to deal with one half.
+    // This table is used to decide when to advance a point on the interior or exterior.
+    // It supports odd TessFactor up to 65 and even TessFactor up to 64.
+    static const int _finalPointPositionTable[33] =
+            { 0, 32, 16, 8, 17, 4, 18, 9, 19, 2, 20, 10, 21, 5, 22, 11, 23,
+              1, 24, 12, 25, 6, 26, 13, 27, 3, 28, 14, 29, 7, 30, 15, 31 };
+    // The loopStart and loopEnd tables below just provide optimal loop bounds for the
+    // stitching algorithm further below, for any given halfTssFactor.
+    // There is probably a better way to encode this...
+
+    // loopStart[halfTessFactor] encodes the FIRST entry other that [0] in finalPointPositionTable[] above which is
+    // less than halfTessFactor.  Exceptions are entry 0 and 1, which are set up to skip the loop.
+    static const int _loopStart[33] =
+            {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};
+    // loopStart[halfTessFactor] encodes the LAST entry in finalPointPositionTable[] above which is
+    // less than halfTessFactor.  Exceptions are entry 0 and 1, which are set up to skip the loop.
+    static const int _loopEnd[33] =
+            {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};
+    const int* finalPointPositionTable;
+    const int* loopStart;
+    const int* loopEnd;
+    if( m_bXBox360Mode )
+    {
+        // The XBox360 vertex introduction order is always from the center of the edge.
+        // So the final positions of points on the half-edge are this trivial table.
+        static const int XBOXfinalPointPositionTable[33] =
+                { 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, 32 };
+        // loopStart and loopEnd (meaning described above) also become trivial for XBox360 splitting.
+        static const int XBOXloopStart[33] =
+                {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};
+        static const int XBOXloopEnd[33] =
+                {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};
+
+        finalPointPositionTable = XBOXfinalPointPositionTable;
+        loopStart = XBOXloopStart;
+        loopEnd = XBOXloopEnd;
+    }
+    else
+    {
+        finalPointPositionTable = _finalPointPositionTable;
+        loopStart = _loopStart;
+        loopEnd =_loopEnd;
+    }
+#else
+    // Tables to assist in the stitching of 2 rows of points having arbitrary TessFactors.
+    // The stitching order is governed by Ruler Function vertex split ordering (see external documentation).
+    //
+    // The contents of the finalPointPositionTable are where vertex i [0..33] ends up on the half-edge
+    // at the max tessellation amount given ruler-function split order.
+    // Recall the other half of an edge is mirrored, so we only need to deal with one half.
+    // This table is used to decide when to advance a point on the interior or exterior.
+    // It supports odd TessFactor up to 65 and even TessFactor up to 64.
+    static const int finalPointPositionTable[33] =
+            { 0, 32, 16, 8, 17, 4, 18, 9, 19, 2, 20, 10, 21, 5, 22, 11, 23,
+              1, 24, 12, 25, 6, 26, 13, 27, 3, 28, 14, 29, 7, 30, 15, 31 };
+
+    // The loopStart and loopEnd tables below just provide optimal loop bounds for the
+    // stitching algorithm further below, for any given halfTssFactor.
+    // There is probably a better way to encode this...
+
+    // loopStart[halfTessFactor] encodes the FIRST entry in finalPointPositionTable[] above which is
+    // less than halfTessFactor.  Exceptions are entry 0 and 1, which are set up to skip the loop.
+    static const int loopStart[33] =
+            {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};
+    // loopStart[halfTessFactor] encodes the LAST entry in finalPointPositionTable[] above which is
+    // less than halfTessFactor.  Exceptions are entry 0 and 1, which are set up to skip the loop.
+    static const int loopEnd[33] =
+            {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};
+#endif
+    if( TESSELLATOR_PARITY_ODD == insideEdgeTessFactorParity )
+    {
+        insideNumHalfTessFactorPoints -= 1;
+    }
+    if( TESSELLATOR_PARITY_ODD == outsideTessFactorParity )
+    {
+        outsideNumHalfTessFactorPoints -= 1;
+    }
+    // Walk first half
+    int outsidePoint = outsideEdgePointBaseOffset;
+    int insidePoint = insideEdgePointBaseOffset;
+
+    // iStart,iEnd are a small optimization so the loop below doesn't have to go from 0 up to 31
+    int iStart = min(loopStart[insideNumHalfTessFactorPoints],loopStart[outsideNumHalfTessFactorPoints]);
+    int iEnd = max(loopEnd[insideNumHalfTessFactorPoints],loopEnd[outsideNumHalfTessFactorPoints]);
+
+    if( finalPointPositionTable[0] < outsideNumHalfTessFactorPoints ) // since we dont' start the loop at 0 below, we need a special case.
+    {
+        // Advance outside
+        DefineClockwiseTriangle(outsidePoint,outsidePoint+1,insidePoint,baseIndexOffset);
+        baseIndexOffset += 3; outsidePoint++;
+    }
+
+    for(int i = iStart; i <= iEnd; i++)
+    {
+        if( /*(i>0) && <-- not needed since iStart is never 0*/(finalPointPositionTable[i] < insideNumHalfTessFactorPoints))
+        {
+            // Advance inside
+            DefineClockwiseTriangle(insidePoint,outsidePoint,insidePoint+1,baseIndexOffset);
+            baseIndexOffset += 3; insidePoint++;
+        }
+        if((finalPointPositionTable[i] < outsideNumHalfTessFactorPoints))
+        {
+            // Advance outside
+            DefineClockwiseTriangle(outsidePoint,outsidePoint+1,insidePoint,baseIndexOffset);
+            baseIndexOffset += 3; outsidePoint++;
+        }
+    }
+
+    if( (insideEdgeTessFactorParity != outsideTessFactorParity) || (insideEdgeTessFactorParity == TESSELLATOR_PARITY_ODD))
+    {
+        if( insideEdgeTessFactorParity == outsideTessFactorParity )
+        {
+            // Quad in the middle
+            DefineClockwiseTriangle(insidePoint,outsidePoint,insidePoint+1,baseIndexOffset);
+            baseIndexOffset += 3;
+            DefineClockwiseTriangle(insidePoint+1,outsidePoint,outsidePoint+1,baseIndexOffset);
+            baseIndexOffset += 3;
+            insidePoint++;
+            outsidePoint++;
+        }
+        else if( TESSELLATOR_PARITY_EVEN == insideEdgeTessFactorParity )
+        {
+            // Triangle pointing inside
+            DefineClockwiseTriangle(insidePoint,outsidePoint,outsidePoint+1,baseIndexOffset);
+            baseIndexOffset += 3;
+            outsidePoint++;
+        }
+        else
+        {
+            // Triangle pointing outside
+            DefineClockwiseTriangle(insidePoint,outsidePoint,insidePoint+1,baseIndexOffset);
+            baseIndexOffset += 3;
+            insidePoint++;
+        }
+    }
+
+    // Walk second half.
+    for(int i = iEnd; i >= iStart; i--)
+    {
+        if((finalPointPositionTable[i] < outsideNumHalfTessFactorPoints))
+        {
+            // Advance outside
+            DefineClockwiseTriangle(outsidePoint,outsidePoint+1,insidePoint,baseIndexOffset);
+            baseIndexOffset += 3; outsidePoint++;
+        }
+        if( /*(i>0) && <-- not needed since iStart is never 0*/ (finalPointPositionTable[i] < insideNumHalfTessFactorPoints))
+        {
+            // Advance inside
+            DefineClockwiseTriangle(insidePoint,outsidePoint,insidePoint+1,baseIndexOffset);
+            baseIndexOffset += 3; insidePoint++;
+        }
+    }
+    // Below case is not needed if we didn't optimize loop above and made it run from 31 down to 0.
+    if((finalPointPositionTable[0] < outsideNumHalfTessFactorPoints))
+    {
+        DefineClockwiseTriangle(outsidePoint,outsidePoint+1,insidePoint,baseIndexOffset);
+        baseIndexOffset += 3; outsidePoint++;
+    }
+}
+
+//---------------------------------------------------------------------------------------------------------------------------------
+// CHWTessellator::PatchIndexValue()
+//--------------------------------------------------------------------------------------------------------------------------------
+int CHWTessellator::PatchIndexValue(int index)
+{
+    if( m_bUsingPatchedIndices )
+    {
+        if( index >= m_IndexPatchContext.outsidePointIndexPatchBase ) // assumed remapped outide indices are > remapped inside vertices
+        {
+            if( index == m_IndexPatchContext.outsidePointIndexBadValue )
+                index = m_IndexPatchContext.outsidePointIndexReplacementValue;
+            else
+                index += m_IndexPatchContext.outsidePointIndexDeltaToRealValue;
+        }
+        else
+        {
+            if( index == m_IndexPatchContext.insidePointIndexBadValue )
+                index = m_IndexPatchContext.insidePointIndexReplacementValue;
+            else
+                index += m_IndexPatchContext.insidePointIndexDeltaToRealValue;
+        }
+    }
+    else if( m_bUsingPatchedIndices2 )
+    {
+        if( index >= m_IndexPatchContext2.baseIndexToInvert )
+        {
+            if( index == m_IndexPatchContext2.cornerCaseBadValue )
+            {
+                index = m_IndexPatchContext2.cornerCaseReplacementValue;
+            }
+            else
+            {
+                index = m_IndexPatchContext2.indexInversionEndPoint - index;
+            }
+        }
+        else if( index == m_IndexPatchContext2.cornerCaseBadValue )
+        {
+            index = m_IndexPatchContext2.cornerCaseReplacementValue;
+        }
+    }
+    return index;
+}
+
+
+//=================================================================================================================================
+// CHLSLTessellator
+//=================================================================================================================================
+
+//---------------------------------------------------------------------------------------------------------------------------------
+// CHLSLTessellator::CHLSLTessellator
+//---------------------------------------------------------------------------------------------------------------------------------
+CHLSLTessellator::CHLSLTessellator()
+{
+    m_LastComputedTessFactors[0] = m_LastComputedTessFactors[1] = m_LastComputedTessFactors[2] =
+    m_LastComputedTessFactors[3] = m_LastComputedTessFactors[4] = m_LastComputedTessFactors[5] = 0;
+}
+
+//---------------------------------------------------------------------------------------------------------------------------------
+// CHLSLTessellator::Init
+// User calls this.
+//---------------------------------------------------------------------------------------------------------------------------------
+void CHLSLTessellator::Init(
+    D3D11_TESSELLATOR_PARTITIONING       partitioning,
+    D3D11_TESSELLATOR_REDUCTION          insideTessFactorReduction,
+    D3D11_TESSELLATOR_QUAD_REDUCTION_AXIS quadInsideTessFactorReductionAxis,
+    D3D11_TESSELLATOR_OUTPUT_PRIMITIVE   outputPrimitive)
+{
+    CHWTessellator::Init(partitioning,outputPrimitive);
+    m_LastComputedTessFactors[0] = m_LastComputedTessFactors[1] = m_LastComputedTessFactors[2] =
+    m_LastComputedTessFactors[3] = m_LastComputedTessFactors[4] = m_LastComputedTessFactors[5] = 0;
+    m_partitioning = partitioning;
+    m_originalPartitioning = partitioning;
+    switch( partitioning )
+    {
+    case D3D11_TESSELLATOR_PARTITIONING_INTEGER:
+    default:
+        break;
+    case D3D11_TESSELLATOR_PARTITIONING_FRACTIONAL_ODD:
+        m_parity = TESSELLATOR_PARITY_ODD;
+        break;
+    case D3D11_TESSELLATOR_PARTITIONING_FRACTIONAL_EVEN:
+        m_parity = TESSELLATOR_PARITY_EVEN;
+        break;
+    }
+    m_originalParity = m_parity;
+    m_outputPrimitive = outputPrimitive;
+    m_insideTessFactorReduction = insideTessFactorReduction;
+    m_quadInsideTessFactorReductionAxis = quadInsideTessFactorReductionAxis;
+}
+//---------------------------------------------------------------------------------------------------------------------------------
+// CHLSLTessellator::TessellateQuadDomain
+// User calls this
+//---------------------------------------------------------------------------------------------------------------------------------
+void CHLSLTessellator::TessellateQuadDomain( float tessFactor_Ueq0, float tessFactor_Veq0, float tessFactor_Ueq1, float tessFactor_Veq1,
+                                         float insideTessFactorScaleU, float insideTessFactorScaleV )
+{
+    QuadHLSLProcessTessFactors(tessFactor_Ueq0,tessFactor_Veq0,tessFactor_Ueq1,tessFactor_Veq1,insideTessFactorScaleU,insideTessFactorScaleV);
+
+    CHWTessellator::TessellateQuadDomain(m_LastComputedTessFactors[0],m_LastComputedTessFactors[1],m_LastComputedTessFactors[2],m_LastComputedTessFactors[3],
+                                         m_LastComputedTessFactors[4],m_LastComputedTessFactors[5]);
+}
+
+//---------------------------------------------------------------------------------------------------------------------------------
+// CHLSLTessellator::QuadHLSLProcessTessFactors
+//---------------------------------------------------------------------------------------------------------------------------------
+void CHLSLTessellator::QuadHLSLProcessTessFactors( float tessFactor_Ueq0, float tessFactor_Veq0, float tessFactor_Ueq1, float tessFactor_Veq1,
+                                               float insideTessFactorScaleU, float insideTessFactorScaleV )
+{
+    if( !(tessFactor_Ueq0 > 0) ||// NaN will pass
+        !(tessFactor_Veq0 > 0) ||
+        !(tessFactor_Ueq1 > 0) ||
+        !(tessFactor_Veq1 > 0) )
+    {
+        m_LastUnRoundedComputedTessFactors[0] = tessFactor_Ueq0;
+        m_LastUnRoundedComputedTessFactors[1] = tessFactor_Veq0;
+        m_LastUnRoundedComputedTessFactors[2] = tessFactor_Ueq1;
+        m_LastUnRoundedComputedTessFactors[3] = tessFactor_Veq1;
+        m_LastUnRoundedComputedTessFactors[4] = 0;
+        m_LastUnRoundedComputedTessFactors[5] = 0;
+        m_LastComputedTessFactors[0] =
+        m_LastComputedTessFactors[1] =
+        m_LastComputedTessFactors[2] =
+        m_LastComputedTessFactors[3] =
+        m_LastComputedTessFactors[4] =
+        m_LastComputedTessFactors[5] = 0;
+        return;
+    }
+
+    CleanupFloatTessFactor(tessFactor_Ueq0);// clamp to [1.0f..INF], NaN->1.0f
+    CleanupFloatTessFactor(tessFactor_Veq0);
+    CleanupFloatTessFactor(tessFactor_Ueq1);
+    CleanupFloatTessFactor(tessFactor_Veq1);
+
+    // Save off tessFactors so they can be returned to app
+    m_LastUnRoundedComputedTessFactors[0] = tessFactor_Ueq0;
+    m_LastUnRoundedComputedTessFactors[1] = tessFactor_Veq0;
+    m_LastUnRoundedComputedTessFactors[2] = tessFactor_Ueq1;
+    m_LastUnRoundedComputedTessFactors[3] = tessFactor_Veq1;
+
+    // Process outside tessFactors
+    float outsideTessFactor[QUAD_EDGES] = {tessFactor_Ueq0, tessFactor_Veq0, tessFactor_Ueq1, tessFactor_Veq1};
+    int edge, axis;
+    TESSELLATOR_PARITY insideTessFactorParity[QUAD_AXES], outsideTessFactorParity[QUAD_EDGES];
+    if( Pow2Partitioning() || IntegerPartitioning() )
+    {
+        for( edge = 0; edge < QUAD_EDGES; edge++ )
+        {
+            RoundUpTessFactor(outsideTessFactor[edge]);
+            ClampTessFactor(outsideTessFactor[edge]); // clamp unbounded user input based on tessellation mode
+            int edgeEven = isEven(outsideTessFactor[edge]);
+            outsideTessFactorParity[edge] = edgeEven ? TESSELLATOR_PARITY_EVEN : TESSELLATOR_PARITY_ODD;
+        }
+    }
+    else
+    {
+        SetTessellationParity(m_originalParity); // ClampTessFactor needs it
+        for( edge = 0; edge < QUAD_EDGES; edge++ )
+        {
+            ClampTessFactor(outsideTessFactor[edge]); // clamp unbounded user input based on tessellation mode
+            outsideTessFactorParity[edge] = m_originalParity;
+        }
+    }
+
+    // Compute inside TessFactors
+    float insideTessFactor[QUAD_AXES];
+    if( m_quadInsideTessFactorReductionAxis == D3D11_TESSELLATOR_QUAD_REDUCTION_1_AXIS )
+    {
+        switch( m_insideTessFactorReduction )
+        {
+        case D3D11_TESSELLATOR_REDUCTION_MIN:
+            insideTessFactor[U] = fmin(fmin(tessFactor_Veq0,tessFactor_Veq1),fmin(tessFactor_Ueq0,tessFactor_Ueq1));
+            break;
+        case D3D11_TESSELLATOR_REDUCTION_MAX:
+            insideTessFactor[U] = fmax(fmax(tessFactor_Veq0,tessFactor_Veq1),fmax(tessFactor_Ueq0,tessFactor_Ueq1));
+            break;
+        case D3D11_TESSELLATOR_REDUCTION_AVERAGE:
+            insideTessFactor[U] = (tessFactor_Veq0 + tessFactor_Veq1 + tessFactor_Ueq0 + tessFactor_Ueq1) / 4;
+            break;
+        }
+        // Scale inside tessFactor based on user scale factor.
+
+        ClampFloatTessFactorScale(insideTessFactorScaleU); // clamp scale value to [0..1], NaN->0
+        insideTessFactor[U] = insideTessFactor[U]*insideTessFactorScaleU;
+
+        // Compute inside parity
+        if( Pow2Partitioning() || IntegerPartitioning() )
+        {
+            ClampTessFactor(insideTessFactor[U]); // clamp reduction + scale result that is based on unbounded user input
+            m_LastUnRoundedComputedTessFactors[4] = m_LastUnRoundedComputedTessFactors[5] = insideTessFactor[U]; // Save off TessFactors so they can be returned to app
+            RoundUpTessFactor(insideTessFactor[U]);
+            insideTessFactorParity[U] =
+            insideTessFactorParity[V] =
+                (isEven(insideTessFactor[U]) || (FLOAT_ONE == insideTessFactor[U]) )
+                ? TESSELLATOR_PARITY_EVEN : TESSELLATOR_PARITY_ODD;
+        }
+        else
+        {
+            ClampTessFactor(insideTessFactor[U]); // clamp reduction + scale result that is based on unbounded user input
+            m_LastUnRoundedComputedTessFactors[4] = m_LastUnRoundedComputedTessFactors[5] = insideTessFactor[U]; // Save off TessFactors so they can be returned to app
+            // no parity changes for fractional tessellation - just use what the user requested
+            insideTessFactorParity[U] = insideTessFactorParity[V] = m_originalParity;
+        }
+
+        // To prevent snapping on edges, the "picture frame" comes
+        // in using avg or max (and ignore inside TessFactor scaling) until it is at least 3.
+        if( (TESSELLATOR_PARITY_ODD == insideTessFactorParity[U]) &&
+            (insideTessFactor[U] < FLOAT_THREE) )
+        {
+            if(D3D11_TESSELLATOR_REDUCTION_MAX == m_insideTessFactorReduction)
+            {
+                insideTessFactor[U] = fmin(FLOAT_THREE,fmax(fmax(tessFactor_Veq0,tessFactor_Veq1),fmax(tessFactor_Ueq0,tessFactor_Ueq1)));
+            }
+            else
+            {
+                insideTessFactor[U] = fmin(FLOAT_THREE,(tessFactor_Veq0 + tessFactor_Veq1 + tessFactor_Ueq0 + tessFactor_Ueq1) / 4);
+            }
+            ClampTessFactor(insideTessFactor[U]); // clamp reduction result that is based on unbounded user input
+            m_LastUnRoundedComputedTessFactors[4] = m_LastUnRoundedComputedTessFactors[5] = insideTessFactor[U]; // Save off TessFactors so they can be returned to app
+            if( IntegerPartitioning())
+            {
+                RoundUpTessFactor(insideTessFactor[U]);
+                insideTessFactorParity[U] =
+                insideTessFactorParity[V] = isEven(insideTessFactor[U]) ? TESSELLATOR_PARITY_EVEN : TESSELLATOR_PARITY_ODD;
+            }
+        }
+        insideTessFactor[V] = insideTessFactor[U];
+    }
+    else
+    {
+        switch( m_insideTessFactorReduction )
+        {
+        case D3D11_TESSELLATOR_REDUCTION_MIN:
+            insideTessFactor[U] = fmin(tessFactor_Veq0,tessFactor_Veq1);
+            insideTessFactor[V] = fmin(tessFactor_Ueq0,tessFactor_Ueq1);
+            break;
+        case D3D11_TESSELLATOR_REDUCTION_MAX:
+            insideTessFactor[U] = fmax(tessFactor_Veq0,tessFactor_Veq1);
+            insideTessFactor[V] = fmax(tessFactor_Ueq0,tessFactor_Ueq1);
+            break;
+        case D3D11_TESSELLATOR_REDUCTION_AVERAGE:
+            insideTessFactor[U] = (tessFactor_Veq0 + tessFactor_Veq1) / 2;
+            insideTessFactor[V] = (tessFactor_Ueq0 + tessFactor_Ueq1) / 2;
+            break;
+        }
+        // Scale inside tessFactors based on user scale factor.
+
+        ClampFloatTessFactorScale(insideTessFactorScaleU); // clamp scale value to [0..1], NaN->0
+        ClampFloatTessFactorScale(insideTessFactorScaleV);
+        insideTessFactor[U] = insideTessFactor[U]*insideTessFactorScaleU;
+        insideTessFactor[V] = insideTessFactor[V]*insideTessFactorScaleV;
+
+        // Compute inside parity
+        if( Pow2Partitioning() || IntegerPartitioning() )
+        {
+            for( axis = 0; axis < QUAD_AXES; axis++ )
+            {
+                ClampTessFactor(insideTessFactor[axis]); // clamp reduction + scale result that is based on unbounded user input
+                m_LastUnRoundedComputedTessFactors[4+axis] = insideTessFactor[axis]; // Save off TessFactors so they can be returned to app
+                RoundUpTessFactor(insideTessFactor[axis]);
+                insideTessFactorParity[axis] =
+                    (isEven(insideTessFactor[axis]) || (FLOAT_ONE == insideTessFactor[axis]) )
+                    ? TESSELLATOR_PARITY_EVEN : TESSELLATOR_PARITY_ODD;
+            }
+        }
+        else
+        {
+            ClampTessFactor(insideTessFactor[U]); // clamp reduction + scale result that is based on unbounded user input
+            ClampTessFactor(insideTessFactor[V]); // clamp reduction + scale result that is based on unbounded user input
+            m_LastUnRoundedComputedTessFactors[4] = insideTessFactor[U]; // Save off TessFactors so they can be returned to app
+            m_LastUnRoundedComputedTessFactors[5] = insideTessFactor[V]; // Save off TessFactors so they can be returned to app
+             // no parity changes for fractional tessellation - just use what the user requested
+            insideTessFactorParity[U] = insideTessFactorParity[V] = m_originalParity;
+        }
+
+        // To prevent snapping on edges, the "picture frame" comes
+        // in using avg or max (and ignore inside TessFactor scaling) until it is at least 3.
+        if( (TESSELLATOR_PARITY_ODD == insideTessFactorParity[U]) &&
+            (insideTessFactor[U] < FLOAT_THREE) )
+        {
+            if(D3D11_TESSELLATOR_REDUCTION_MAX == m_insideTessFactorReduction)
+            {
+                insideTessFactor[U] = fmin(FLOAT_THREE,fmax(tessFactor_Veq0,tessFactor_Veq1));
+            }
+            else
+            {
+                insideTessFactor[U] = fmin(FLOAT_THREE,(tessFactor_Veq0 + tessFactor_Veq1) / 2);
+            }
+            ClampTessFactor(insideTessFactor[U]); // clamp reduction result that is based on unbounded user input
+            m_LastUnRoundedComputedTessFactors[4] = insideTessFactor[U]; // Save off TessFactors so they can be returned to app
+            if( IntegerPartitioning())
+            {
+                RoundUpTessFactor(insideTessFactor[U]);
+                insideTessFactorParity[U] = isEven(insideTessFactor[U]) ? TESSELLATOR_PARITY_EVEN : TESSELLATOR_PARITY_ODD;
+            }
+        }
+
+        if( (TESSELLATOR_PARITY_ODD == insideTessFactorParity[V]) &&
+            (insideTessFactor[V] < FLOAT_THREE) )
+        {
+            if(D3D11_TESSELLATOR_REDUCTION_MAX == m_insideTessFactorReduction)
+            {
+                insideTessFactor[V] = fmin(FLOAT_THREE,fmax(tessFactor_Ueq0,tessFactor_Ueq1));
+            }
+            else
+            {
+                insideTessFactor[V] = fmin(FLOAT_THREE,(tessFactor_Ueq0 + tessFactor_Ueq1) / 2);
+            }
+            ClampTessFactor(insideTessFactor[V]);// clamp reduction result that is based on unbounded user input
+            m_LastUnRoundedComputedTessFactors[5] = insideTessFactor[V]; // Save off TessFactors so they can be returned to app
+            if( IntegerPartitioning())
+            {
+                RoundUpTessFactor(insideTessFactor[V]);
+                insideTessFactorParity[V] = isEven(insideTessFactor[V]) ? TESSELLATOR_PARITY_EVEN : TESSELLATOR_PARITY_ODD;
+            }
+        }
+
+        for( axis = 0; axis < QUAD_AXES; axis++ )
+        {
+            if( TESSELLATOR_PARITY_ODD == insideTessFactorParity[axis] )
+            {
+                // Ensure the first ring ("picture frame") interpolates in on all sides
+                // as much as the side with the minimum TessFactor.  Prevents snapping to edge.
+                if( (insideTessFactor[axis] < FLOAT_THREE) && (insideTessFactor[axis] < insideTessFactor[(axis+1)&0x1]))
+                {
+                    insideTessFactor[axis] = fmin(insideTessFactor[(axis+1)&0x1],FLOAT_THREE);
+                    m_LastUnRoundedComputedTessFactors[4+axis] = insideTessFactor[axis]; // Save off TessFactors so they can be returned to app
+                }
+            }
+        }
+    }
+
+    // Save off TessFactors so they can be returned to app
+    m_LastComputedTessFactors[0] = outsideTessFactor[Ueq0];
+    m_LastComputedTessFactors[1] = outsideTessFactor[Veq0];
+    m_LastComputedTessFactors[2] = outsideTessFactor[Ueq1];
+    m_LastComputedTessFactors[3] = outsideTessFactor[Veq1];
+    m_LastComputedTessFactors[4] = insideTessFactor[U];
+    m_LastComputedTessFactors[5] = insideTessFactor[V];
+}
+
+//---------------------------------------------------------------------------------------------------------------------------------
+// CHLSLTessellator::TessellateTriDomain
+// User calls this
+//---------------------------------------------------------------------------------------------------------------------------------
+void CHLSLTessellator::TessellateTriDomain( float tessFactor_Ueq0, float tessFactor_Veq0, float tessFactor_Weq0,
+                                        float insideTessFactorScale )
+{
+    TriHLSLProcessTessFactors(tessFactor_Ueq0,tessFactor_Veq0,tessFactor_Weq0,insideTessFactorScale);
+
+    CHWTessellator::TessellateTriDomain(m_LastComputedTessFactors[0],m_LastComputedTessFactors[1],m_LastComputedTessFactors[2],m_LastComputedTessFactors[3]);
+}
+
+//---------------------------------------------------------------------------------------------------------------------------------
+// CHLSLTessellator::TriHLSLProcessTessFactors
+//---------------------------------------------------------------------------------------------------------------------------------
+void CHLSLTessellator::TriHLSLProcessTessFactors( float tessFactor_Ueq0, float tessFactor_Veq0, float tessFactor_Weq0,
+                                  float insideTessFactorScale )
+{
+    if( !(tessFactor_Ueq0 > 0) || // NaN will pass
+        !(tessFactor_Veq0 > 0) ||
+        !(tessFactor_Weq0 > 0) )
+    {
+        m_LastUnRoundedComputedTessFactors[0] = tessFactor_Ueq0;
+        m_LastUnRoundedComputedTessFactors[1] = tessFactor_Veq0;
+        m_LastUnRoundedComputedTessFactors[2] = tessFactor_Weq0;
+        m_LastUnRoundedComputedTessFactors[3] =
+        m_LastComputedTessFactors[0] =
+        m_LastComputedTessFactors[1] =
+        m_LastComputedTessFactors[2] =
+        m_LastComputedTessFactors[3] = 0;
+        return;
+    }
+
+    CleanupFloatTessFactor(tessFactor_Ueq0); // clamp to [1.0f..INF], NaN->1.0f
+    CleanupFloatTessFactor(tessFactor_Veq0);
+    CleanupFloatTessFactor(tessFactor_Weq0);
+
+    // Save off TessFactors so they can be returned to app
+    m_LastUnRoundedComputedTessFactors[0] = tessFactor_Ueq0;
+    m_LastUnRoundedComputedTessFactors[1] = tessFactor_Veq0;
+    m_LastUnRoundedComputedTessFactors[2] = tessFactor_Weq0;
+
+    // Process outside TessFactors
+    float outsideTessFactor[TRI_EDGES] = {tessFactor_Ueq0, tessFactor_Veq0, tessFactor_Weq0};
+    int edge;
+    if( Pow2Partitioning() || IntegerPartitioning() )
+    {
+        for( edge = 0; edge < TRI_EDGES; edge++ )
+        {
+            RoundUpTessFactor(outsideTessFactor[edge]); // for pow2 this rounds to pow2
+            ClampTessFactor(outsideTessFactor[edge]); // clamp unbounded user input based on tessellation mode
+        }
+    }
+    else
+    {
+        for( edge = 0; edge < TRI_EDGES; edge++ )
+        {
+            ClampTessFactor(outsideTessFactor[edge]); // clamp unbounded user input based on tessellation mode
+        }
+    }
+
+    // Compute inside TessFactor
+    float insideTessFactor;
+    switch( m_insideTessFactorReduction )
+    {
+    case D3D11_TESSELLATOR_REDUCTION_MIN:
+        insideTessFactor = fmin(fmin(tessFactor_Ueq0,tessFactor_Veq0),tessFactor_Weq0);
+        break;
+    case D3D11_TESSELLATOR_REDUCTION_MAX:
+        insideTessFactor = fmax(fmax(tessFactor_Ueq0,tessFactor_Veq0),tessFactor_Weq0);
+        break;
+    case D3D11_TESSELLATOR_REDUCTION_AVERAGE:
+        insideTessFactor = (tessFactor_Ueq0 + tessFactor_Veq0 + tessFactor_Weq0) / 3;
+        break;
+    }
+
+    // Scale inside TessFactor based on user scale factor.
+    ClampFloatTessFactorScale(insideTessFactorScale); // clamp scale value to [0..1], NaN->0
+    insideTessFactor = insideTessFactor*fmin(FLOAT_ONE,insideTessFactorScale);
+
+    ClampTessFactor(insideTessFactor); // clamp reduction + scale result that is based on unbounded user input
+    m_LastUnRoundedComputedTessFactors[3] = insideTessFactor;// Save off TessFactors so they can be returned to app
+    TESSELLATOR_PARITY parity;
+    if( Pow2Partitioning() || IntegerPartitioning() )
+    {
+        RoundUpTessFactor(insideTessFactor);
+        parity = (isEven(insideTessFactor) || (FLOAT_ONE == insideTessFactor))
+                                        ? TESSELLATOR_PARITY_EVEN : TESSELLATOR_PARITY_ODD;
+    }
+    else
+    {
+        parity = m_originalParity;
+    }
+
+    if( (TESSELLATOR_PARITY_ODD == parity) &&
+        (insideTessFactor < FLOAT_THREE))
+    {
+        // To prevent snapping on edges, the "picture frame" comes
+        // in using avg or max (and ignore inside TessFactor scaling) until it is at least 3.
+        if(D3D11_TESSELLATOR_REDUCTION_MAX == m_insideTessFactorReduction)
+        {
+            insideTessFactor = fmin(FLOAT_THREE,fmax(tessFactor_Ueq0,fmax(tessFactor_Veq0,tessFactor_Weq0)));
+        }
+        else
+        {
+            insideTessFactor = fmin(FLOAT_THREE,(tessFactor_Ueq0 + tessFactor_Veq0 + tessFactor_Weq0) / 3);
+        }
+        ClampTessFactor(insideTessFactor); // clamp reduction result that is based on unbounded user input
+        m_LastUnRoundedComputedTessFactors[3] = insideTessFactor;// Save off TessFactors so they can be returned to app
+        if( IntegerPartitioning())
+        {
+            RoundUpTessFactor(insideTessFactor);
+        }
+    }
+
+    // Save off TessFactors so they can be returned to app
+    m_LastComputedTessFactors[0] = outsideTessFactor[Ueq0];
+    m_LastComputedTessFactors[1] = outsideTessFactor[Veq0];
+    m_LastComputedTessFactors[2] = outsideTessFactor[Weq0];
+    m_LastComputedTessFactors[3] = insideTessFactor;
+}
+
+//---------------------------------------------------------------------------------------------------------------------------------
+// CHLSLTessellator::TessellateIsoLineDomain
+// User calls this.
+//---------------------------------------------------------------------------------------------------------------------------------
+void CHLSLTessellator::TessellateIsoLineDomain( float TessFactor_U_LineDetail, float TessFactor_V_LineDensity )
+{
+    IsoLineHLSLProcessTessFactors(TessFactor_V_LineDensity,TessFactor_U_LineDetail);
+    CHWTessellator::TessellateIsoLineDomain(m_LastComputedTessFactors[0],m_LastComputedTessFactors[1]);
+}
+
+//---------------------------------------------------------------------------------------------------------------------------------
+// CHLSLTessellator::IsoLineHLSLProcessTessFactors
+//---------------------------------------------------------------------------------------------------------------------------------
+void CHLSLTessellator::IsoLineHLSLProcessTessFactors( float TessFactor_V_LineDensity, float TessFactor_U_LineDetail )
+{
+    if( !(TessFactor_V_LineDensity > 0) || // NaN will pass
+        !(TessFactor_U_LineDetail > 0) )
+    {
+        m_LastUnRoundedComputedTessFactors[0] = TessFactor_V_LineDensity;
+        m_LastUnRoundedComputedTessFactors[1] = TessFactor_U_LineDetail;
+        m_LastComputedTessFactors[0] =
+        m_LastComputedTessFactors[1] = 0;
+        return;
+    }
+
+    CleanupFloatTessFactor(TessFactor_V_LineDensity); // clamp to [1.0f..INF], NaN->1.0f
+    CleanupFloatTessFactor(TessFactor_U_LineDetail); // clamp to [1.0f..INF], NaN->1.0f
+
+    ClampTessFactor(TessFactor_U_LineDetail); // clamp unbounded user input based on tessellation mode
+
+    m_LastUnRoundedComputedTessFactors[1] = TessFactor_U_LineDetail;    // Save off TessFactors so they can be returned to app
+
+    TESSELLATOR_PARITY parity;
+    if(Pow2Partitioning()||IntegerPartitioning())
+    {
+        RoundUpTessFactor(TessFactor_U_LineDetail);
+        parity = isEven(TessFactor_U_LineDetail) ? TESSELLATOR_PARITY_EVEN : TESSELLATOR_PARITY_ODD;
+    }
+    else
+    {
+        parity = m_originalParity;
+    }
+
+    FXP fxpTessFactor_U_LineDetail = floatToFixed(TessFactor_U_LineDetail);
+
+    OverridePartitioning(D3D11_TESSELLATOR_PARTITIONING_INTEGER);
+
+    ClampTessFactor(TessFactor_V_LineDensity); // Clamp unbounded user input to integer
+    m_LastUnRoundedComputedTessFactors[0] = TessFactor_V_LineDensity;    // Save off TessFactors so they can be returned to app
+
+    RoundUpTessFactor(TessFactor_V_LineDensity);
+
+    RestorePartitioning();
+
+    // Save off TessFactors so they can be returned to app
+    m_LastComputedTessFactors[0] = TessFactor_V_LineDensity;
+    m_LastComputedTessFactors[1] = TessFactor_U_LineDetail;
+}
+
+//---------------------------------------------------------------------------------------------------------------------------------
+// CHLSLTessellator::ClampTessFactor()
+//---------------------------------------------------------------------------------------------------------------------------------
+void CHLSLTessellator::ClampTessFactor(float& TessFactor)
+{
+    if( Pow2Partitioning() )
+    {
+        TessFactor = fmin( D3D11_TESSELLATOR_MAX_EVEN_TESSELLATION_FACTOR, fmax( TessFactor, D3D11_TESSELLATOR_MIN_ODD_TESSELLATION_FACTOR) );
+    }
+    else if( IntegerPartitioning() )
+    {
+        TessFactor = fmin( D3D11_TESSELLATOR_MAX_TESSELLATION_FACTOR, fmax( TessFactor, D3D11_TESSELLATOR_MIN_ODD_TESSELLATION_FACTOR) );
+    }
+    else if( Odd() )
+    {
+        TessFactor = fmin( D3D11_TESSELLATOR_MAX_ODD_TESSELLATION_FACTOR, fmax( TessFactor, D3D11_TESSELLATOR_MIN_ODD_TESSELLATION_FACTOR) );
+    }
+    else // even
+    {
+        TessFactor = fmin( D3D11_TESSELLATOR_MAX_EVEN_TESSELLATION_FACTOR, fmax( TessFactor, D3D11_TESSELLATOR_MIN_EVEN_TESSELLATION_FACTOR) );
+    }
+}
+
+//---------------------------------------------------------------------------------------------------------------------------------
+// CHLSLTessellator::CleanupFloatTessFactor()
+//---------------------------------------------------------------------------------------------------------------------------------
+static const int exponentMask = 0x7f800000;
+static const int mantissaMask = 0x007fffff;
+void CHLSLTessellator::CleanupFloatTessFactor(float& input)
+{
+    // If input is < 1.0f or NaN, clamp to 1.0f.
+    // In other words, clamp input to [1.0f...+INF]
+    int bits = *(int*)&input;
+    if( ( ( ( bits & exponentMask ) == exponentMask ) && ( bits & mantissaMask ) ) ||// nan?
+        (input < 1.0f) )
+    {
+        input = 1;
+    }
+}
+
+//---------------------------------------------------------------------------------------------------------------------------------
+// CHLSLTessellator::ClampFloatTessFactorScale()
+//---------------------------------------------------------------------------------------------------------------------------------
+void CHLSLTessellator::ClampFloatTessFactorScale(float& input)
+{
+    // If input is < 0.0f or NaN, clamp to 0.0f.  > 1 clamps to 1.
+    // In other words, clamp input to [0.0f...1.0f]
+    int bits = *(int*)&input;
+    if( ( ( ( bits & exponentMask ) == exponentMask ) && ( bits & mantissaMask ) ) ||// nan?
+        (input < 0.0f) )
+    {
+        input = 0;
+    }
+    else if( input > 1 )
+    {
+        input = 1;
+    }
+}
+
+//---------------------------------------------------------------------------------------------------------------------------------
+// CHLSLTessellator::RoundUpTessFactor()
+//---------------------------------------------------------------------------------------------------------------------------------
+static const int exponentLSB = 0x00800000;
+void CHLSLTessellator::RoundUpTessFactor(float& TessFactor)
+{
+    // Assume TessFactor is in [1.0f..+INF]
+    if( Pow2Partitioning() )
+    {
+        int bits = *(int*)&TessFactor;
+        if( bits & mantissaMask )
+        {
+            *(int*)&TessFactor = (bits & exponentMask) + exponentLSB;
+        }
+    }
+    else if( IntegerPartitioning() )
+    {
+        TessFactor = ceil(TessFactor);
+    }
+}
index 348170bfd4245e2d2a429a8a10127599c4cce141..04ae0237fef519324c3fc9d812ca0e88ec72fd96 100644 (file)
@@ -1,9 +1,5 @@
 /****************************************************************************
- * Copyright (C) 2014-2015 Intel Corporation.   All Rights Reserved.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
+ * Copyright (C) 2014-2019 without restriction, including without limitation
  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
  * and/or sell copies of the Software, and to permit persons to whom the
  * Software is furnished to do so, subject to the following conditions:
  ******************************************************************************/
 #pragma once
 
-/// Allocate and initialize a new tessellation context
-HANDLE SWR_API
-       TSInitCtx(SWR_TS_DOMAIN          tsDomain, ///< [IN] Tessellation domain (isoline, quad, triangle)
-                 SWR_TS_PARTITIONING    tsPartitioning, ///< [IN] Tessellation partitioning algorithm
-                 SWR_TS_OUTPUT_TOPOLOGY tsOutputTopology, ///< [IN] Tessellation output topology
-                 void*                  pContextMem,      ///< [IN] Memory to use for the context
-                 size_t& memSize); ///< [INOUT] In: Amount of memory in pContextMem. Out: Mem required
-
-/// Destroy & de-allocate tessellation context
-void SWR_API TSDestroyCtx(HANDLE tsCtx); ///< [IN] Tessellation context to be destroyed
+#include "tessellator.hpp"
 
 struct SWR_TS_TESSELLATED_DATA
 {
@@ -49,34 +36,170 @@ struct SWR_TS_TESSELLATED_DATA
     // For Tri: pDomainPointsW[i] = 1.0f - pDomainPointsU[i] - pDomainPointsV[i]
 };
 
-/// Perform Tessellation
-void SWR_API
-     TSTessellate(HANDLE                          tsCtx,         ///< [IN] Tessellation Context
-                  const SWR_TESSELLATION_FACTORS& tsTessFactors, ///< [IN] Tessellation Factors
-                  SWR_TS_TESSELLATED_DATA&        tsTessellatedData);   ///< [OUT] Tessellated Data
+namespace Tessellator
+{
+    /// Wrapper class for the CHWTessellator reference tessellator from MSFT
+    /// This class will store data not originally stored in CHWTessellator
+    class SWR_TS : private CHWTessellator
+    {
+    private:
+        typedef CHWTessellator SUPER;
+        SWR_TS_DOMAIN          Domain;
+        OSALIGNSIMD(float)     DomainPointsU[MAX_POINT_COUNT];
+        OSALIGNSIMD(float)     DomainPointsV[MAX_POINT_COUNT];
+        uint32_t               NumDomainPoints;
+        OSALIGNSIMD(uint32_t)  Indices[3][MAX_INDEX_COUNT / 3];
+        uint32_t               NumIndices;
+
+    public:
+        void Init(SWR_TS_DOMAIN          tsDomain,
+                  SWR_TS_PARTITIONING    tsPartitioning,
+                  SWR_TS_OUTPUT_TOPOLOGY tsOutputTopology)
+        {
+            static D3D11_TESSELLATOR_PARTITIONING CVT_TS_D3D_PARTITIONING[] = {
+                D3D11_TESSELLATOR_PARTITIONING_INTEGER,         // SWR_TS_INTEGER
+                D3D11_TESSELLATOR_PARTITIONING_FRACTIONAL_ODD,  // SWR_TS_ODD_FRACTIONAL
+                D3D11_TESSELLATOR_PARTITIONING_FRACTIONAL_EVEN, // SWR_TS_EVEN_FRACTIONAL
+                D3D11_TESSELLATOR_PARTITIONING_POW2,            // SWR_TS_POW2
+            };
+
+            static D3D11_TESSELLATOR_OUTPUT_PRIMITIVE CVT_TS_D3D_OUTPUT_TOPOLOGY[] = {
+                D3D11_TESSELLATOR_OUTPUT_POINT,        // SWR_TS_OUTPUT_POINT
+                D3D11_TESSELLATOR_OUTPUT_LINE,         // SWR_TS_OUTPUT_LINE
+                D3D11_TESSELLATOR_OUTPUT_TRIANGLE_CW,  // SWR_TS_OUTPUT_TRI_CW
+                D3D11_TESSELLATOR_OUTPUT_TRIANGLE_CCW, // SWR_TS_OUTPUT_TRI_CCW
+            };
+
+            SUPER::Init(CVT_TS_D3D_PARTITIONING[tsPartitioning],
+                        CVT_TS_D3D_OUTPUT_TOPOLOGY[tsOutputTopology]);
+
+            Domain          = tsDomain;
+            NumDomainPoints = 0;
+            NumIndices      = 0;
+        }
+
+        void Tessellate(const SWR_TESSELLATION_FACTORS& tsTessFactors,
+                        SWR_TS_TESSELLATED_DATA&        tsTessellatedData)
+        {
+            uint32_t IndexDiv = 0;
+            switch (Domain)
+            {
+            case SWR_TS_QUAD:
+                IndexDiv = 3;
+                SUPER::TessellateQuadDomain(
+                    tsTessFactors.OuterTessFactors[SWR_QUAD_U_EQ0_TRI_U_LINE_DETAIL],
+                    tsTessFactors.OuterTessFactors[SWR_QUAD_V_EQ0_TRI_W],
+                    tsTessFactors.OuterTessFactors[SWR_QUAD_U_EQ1_TRI_V_LINE_DENSITY],
+                    tsTessFactors.OuterTessFactors[SWR_QUAD_V_EQ1],
+                    tsTessFactors.InnerTessFactors[SWR_QUAD_U_TRI_INSIDE],
+                    tsTessFactors.InnerTessFactors[SWR_QUAD_V_INSIDE]);
+                break;
+
+            case SWR_TS_TRI:
+                IndexDiv = 3;
+                SUPER::TessellateTriDomain(
+                    tsTessFactors.OuterTessFactors[SWR_QUAD_U_EQ0_TRI_U_LINE_DETAIL],
+                    tsTessFactors.OuterTessFactors[SWR_QUAD_U_EQ1_TRI_V_LINE_DENSITY],
+                    tsTessFactors.OuterTessFactors[SWR_QUAD_V_EQ0_TRI_W],
+                    tsTessFactors.InnerTessFactors[SWR_QUAD_U_TRI_INSIDE]);
+                break;
+
+            case SWR_TS_ISOLINE:
+                IndexDiv = 2;
+                SUPER::TessellateIsoLineDomain(
+                    tsTessFactors.OuterTessFactors[SWR_QUAD_U_EQ1_TRI_V_LINE_DENSITY],
+                    tsTessFactors.OuterTessFactors[SWR_QUAD_U_EQ0_TRI_U_LINE_DETAIL]);
+                break;
+
+            default:
+                SWR_INVALID("Invalid Tessellation Domain: %d", Domain);
+            }
+
+            NumDomainPoints = (uint32_t)SUPER::GetPointCount();
 
+            DOMAIN_POINT* pPoints = SUPER::GetPoints();
+            for (uint32_t i = 0; i < NumDomainPoints; i++) {
+                DomainPointsU[i] = pPoints[i].u;
+                DomainPointsV[i] = pPoints[i].v;
+            }
+            tsTessellatedData.NumDomainPoints = NumDomainPoints;
+            tsTessellatedData.pDomainPointsU  = &DomainPointsU[0];
+            tsTessellatedData.pDomainPointsV  = &DomainPointsV[0];
 
-/// @TODO - Implement OSS tessellator
+            NumIndices = (uint32_t)SUPER::GetIndexCount();
 
-INLINE HANDLE SWR_API TSInitCtx(SWR_TS_DOMAIN          tsDomain,
-                                SWR_TS_PARTITIONING    tsPartitioning,
-                                SWR_TS_OUTPUT_TOPOLOGY tsOutputTopology,
-                                void*                  pContextMem,
-                                size_t&                memSize)
+            assert(NumIndices % IndexDiv == 0);
+            tsTessellatedData.NumPrimitives = NumIndices / IndexDiv;
+
+            uint32_t* pIndices = (uint32_t*)SUPER::GetIndices();
+            for (uint32_t i = 0; i < NumIndices; i++) {
+                Indices[i % IndexDiv][i / IndexDiv] = pIndices[i];
+            }
+
+            tsTessellatedData.ppIndices[0] = &Indices[0][0];
+            tsTessellatedData.ppIndices[1] = &Indices[1][0];
+            tsTessellatedData.ppIndices[2] = &Indices[2][0];
+        }
+    };
+} // namespace Tessellator
+
+/// Allocate and initialize a new tessellation context
+INLINE HANDLE SWR_API
+              TSInitCtx(SWR_TS_DOMAIN          tsDomain, ///< [IN] Tessellation domain (isoline, quad, triangle)
+                        SWR_TS_PARTITIONING    tsPartitioning, ///< [IN] Tessellation partitioning algorithm
+                        SWR_TS_OUTPUT_TOPOLOGY tsOutputTopology, ///< [IN] Tessellation output topology
+                        void*                  pContextMem, ///< [IN] Memory to use for the context
+                        size_t& memSize) ///< [INOUT] In: Amount of memory in pContextMem. Out: Mem required
 {
-    SWR_NOT_IMPL;
-    return NULL;
+    using Tessellator::SWR_TS;
+    SWR_ASSERT(tsDomain < SWR_TS_DOMAIN_COUNT);
+    SWR_ASSERT(tsPartitioning < SWR_TS_PARTITIONING_COUNT);
+    SWR_ASSERT(tsOutputTopology < SWR_TS_OUTPUT_TOPOLOGY_COUNT);
+
+    size_t origMemSize = memSize;
+    memSize            = AlignUp(sizeof(SWR_TS), 64);
+
+    if (nullptr == pContextMem || memSize > origMemSize)
+    {
+        return nullptr;
+    }
+
+    HANDLE tsCtx = pContextMem;
+    if (!tsCtx)
+    {
+        return tsCtx;
+    }
+
+    SWR_TS* pTessellator = new (tsCtx) SWR_TS();
+    SWR_ASSERT(pTessellator == tsCtx);
+
+    pTessellator->Init(tsDomain, tsPartitioning, tsOutputTopology);
+
+    return tsCtx;
 }
 
-INLINE void SWR_API TSDestroyCtx(HANDLE tsCtx)
+/// Destroy & de-allocate tessellation context
+INLINE void SWR_API TSDestroyCtx(HANDLE tsCtx) ///< [IN] Tessellation context to be destroyed
 {
-    SWR_NOT_IMPL;
+    using Tessellator::SWR_TS;
+    SWR_TS* pTessellator = (SWR_TS*)tsCtx;
+
+    if (pTessellator)
+    {
+        pTessellator->~SWR_TS();
+    }
 }
 
-INLINE void SWR_API TSTessellate(HANDLE                          tsCtx,
-                                 const SWR_TESSELLATION_FACTORS& tsTessFactors,
-                                 SWR_TS_TESSELLATED_DATA&        tsTessellatedData)
+/// Perform Tessellation
+INLINE void SWR_API
+            TSTessellate(HANDLE                          tsCtx, ///< [IN] Tessellation Context
+                         const SWR_TESSELLATION_FACTORS& tsTessFactors, ///< [IN] Tessellation Factors
+                         SWR_TS_TESSELLATED_DATA&        tsTessellatedData)    ///< [OUT] Tessellated Data
 {
-    SWR_NOT_IMPL;
+    using Tessellator::SWR_TS;
+    SWR_TS* pTessellator = (SWR_TS*)tsCtx;
+    SWR_ASSERT(pTessellator);
+
+    pTessellator->Tessellate(tsTessFactors, tsTessellatedData);
 }
 
diff --git a/src/gallium/drivers/swr/rasterizer/core/tessellator.hpp b/src/gallium/drivers/swr/rasterizer/core/tessellator.hpp
new file mode 100644 (file)
index 0000000..77a228d
--- /dev/null
@@ -0,0 +1,471 @@
+/*
+    Copyright (c) Microsoft Corporation
+
+    Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
+    associated documentation files (the "Software"), to deal in the Software without restriction,
+    including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+    and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+    subject to the following conditions:
+
+    The above copyright notice and this permission notice shall be included in all copies or substantial
+    portions of the Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
+    NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+    IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+    WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+    SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#pragma once
+//=================================================================================================================================
+// Microsoft D3D11 Fixed Function Tessellator Reference - May 7, 2012
+// amar.patel@microsoft.com
+//
+// CHWTessellator demonstrates what is expected of hardware in the D3D11 fixed function Tessellator stage.  Hardware
+// implementers need only look at this class.
+//
+// CHLSLTessellator is a wrapper for CHWTessellator, representing the effect of shader code that will
+// be autogenerated by HLSL in the Hull Shader, both for plumbing data around, and to precondition TessFactor values before they
+// are passed to the hardware (such as deriving inside TessFactors from edge TessFactors).  The algorithms used
+// in CHLSLTessellator are subject to change, but since they represent shader code auto-generated by the HLSL compiler,
+// CHLSLTessellator has no effect on hardware design at all.  Note the HLSL compiler will expose all the raw hardware
+// control illustrated by CHWTessellator for those who don't need the helper functionality illustrated by CHLSLTessellator.
+//
+// Usage:        (1) Create either a CHLSLTessellator or CHWTessellator object, depending on which you want to verify.
+//               (2) Call C*Tessellator::Init()
+//               (3) Call C*Tessellator::Tessellate[IsoLine|Tri|Quad]Domain()
+//                      - Here you pass in TessFactors (how much to tessellate)
+//               (4) Call C*Tessellator::GetPointCount(), C*Tessellator::GetIndexCount() to see how much data was generated.
+//               (5) Call C*Tessellator::GetPoints() and C*Tessellator::GetIndices() to get pointers to the data.
+//                   The pointers are fixed for the lifetime of the object (storage for max tessellation),
+//                   so if you ::Tessellate again, the data in the buffers is overwritten.
+//               (6) There are various other Get() methods to retrieve TessFactors that have been processed from
+//                   what you passed in at step 3.  You can retrieve separate TessFactors that the tessellator
+//                   produced after clamping but before rounding, and also after rounding (say in pow2 mode).
+//                   These numbers can be useful information if you are geomorphing displacement maps.
+//               (7) Goto Step 2 or 3 if you want to animate TessFactors or tessellate a different patch
+//
+// Code implementation details:
+//
+// There is lots of headroom to make this code run faster on CPUs.  It was written merely as a reference for
+// what results hardware should produce, with CPU performance not a consideration.  It is nice that this implementation
+// only generates the exact number of vertices needed (no duplicates) in the output vertex buffer.  Also, the number
+// of calculations done for each U/V domain coordinate is minimized by doing some precalculation of some patch or edge
+// invariant numbers (see TESS_FACTOR_CONTEXT).  All the vertex coordinate calculations could be computed with as much
+// parallelism as you like.  Similarly the calculation of connectivity itself is highly parallelizable, and can also
+// be done independent of the vertex calculations.
+//
+//=================================================================================================================================
+
+#define D3D11_TESSELLATOR_MIN_ODD_TESSELLATION_FACTOR 1
+#define D3D11_TESSELLATOR_MAX_ODD_TESSELLATION_FACTOR 63
+#define D3D11_TESSELLATOR_MIN_EVEN_TESSELLATION_FACTOR 2
+#define D3D11_TESSELLATOR_MAX_EVEN_TESSELLATION_FACTOR 64
+
+#define D3D11_TESSELLATOR_MIN_ISOLINE_DENSITY_TESSELLATION_FACTOR 1
+#define D3D11_TESSELLATOR_MAX_ISOLINE_DENSITY_TESSELLATION_FACTOR 64
+
+#define D3D11_TESSELLATOR_MAX_TESSELLATION_FACTOR 64 // max of even and odd tessFactors
+
+#define MAX_POINT_COUNT ((D3D11_TESSELLATOR_MAX_TESSELLATION_FACTOR+1)*(D3D11_TESSELLATOR_MAX_TESSELLATION_FACTOR+1))
+#define MAX_INDEX_COUNT (D3D11_TESSELLATOR_MAX_TESSELLATION_FACTOR*D3D11_TESSELLATOR_MAX_TESSELLATION_FACTOR*2*3)
+
+//=================================================================================================================================
+// Data types for the caller
+//=================================================================================================================================
+typedef enum D3D11_TESSELLATOR_PARTITIONING
+{
+    D3D11_TESSELLATOR_PARTITIONING_INTEGER,
+    D3D11_TESSELLATOR_PARTITIONING_POW2,
+    D3D11_TESSELLATOR_PARTITIONING_FRACTIONAL_ODD,
+    D3D11_TESSELLATOR_PARTITIONING_FRACTIONAL_EVEN
+};
+
+typedef enum D3D11_TESSELLATOR_REDUCTION
+{
+    D3D11_TESSELLATOR_REDUCTION_MIN,
+    D3D11_TESSELLATOR_REDUCTION_MAX,
+    D3D11_TESSELLATOR_REDUCTION_AVERAGE
+};
+
+typedef enum D3D11_TESSELLATOR_QUAD_REDUCTION_AXIS
+{
+    D3D11_TESSELLATOR_QUAD_REDUCTION_1_AXIS,
+    D3D11_TESSELLATOR_QUAD_REDUCTION_2_AXIS
+};
+
+typedef enum D3D11_TESSELLATOR_OUTPUT_PRIMITIVE
+{
+    D3D11_TESSELLATOR_OUTPUT_POINT,
+    D3D11_TESSELLATOR_OUTPUT_LINE,
+    D3D11_TESSELLATOR_OUTPUT_TRIANGLE_CW,
+    D3D11_TESSELLATOR_OUTPUT_TRIANGLE_CCW,
+};
+
+typedef struct DOMAIN_POINT
+{
+    float u;
+    float v; // for tri, w = 1 - u - v;
+} DOMAIN_POINT;
+
+//=================================================================================================================================
+// CHWTessellator: D3D11 Tessellation Fixed Function Hardware Reference
+//=================================================================================================================================
+typedef unsigned int FXP; // fixed point number
+
+class CHWTessellator
+{
+
+//---------------------------------------------------------------------------------------------------------------------------------
+public:
+    void Init( D3D11_TESSELLATOR_PARTITIONING         partitioning,
+               D3D11_TESSELLATOR_OUTPUT_PRIMITIVE     outputPrimitive);
+
+    void TessellateIsoLineDomain( float TessFactor_V_LineDensity,
+                                  float TessFactor_U_LineDetail );
+
+    void TessellateTriDomain( float TessFactor_Ueq0,
+                              float TessFactor_Veq0,
+                              float TessFactor_Weq0,
+                              float TessFactor_Inside );
+
+    void TessellateQuadDomain( float TessFactor_Ueq0,
+                               float TessFactor_Veq0,
+                               float TessFactor_Ueq1,
+                               float TessFactor_Veq1,
+                               float TessFactor_InsideU,
+                               float TessFactor_InsideV );
+
+    int GetPointCount();
+    int GetIndexCount();
+
+    DOMAIN_POINT* GetPoints(); // Get CHWTessellator owned pointer to vertices (UV values).
+                               // Pointer is fixed for lifetime of CHWTessellator object.
+    int* GetIndices();         // Get CHWTessellator owned pointer to vertex indices.
+                               // Pointer is fixed for lifetime of CHWTessellator object.
+
+#define ALLOW_XBOX_360_COMPARISON // Different vertex splitting order. This is NOT D3D11 behavior, just available here for comparison.
+                                     // Setting this define true just allows the XBox split style to be enabled via
+                                     // SetXBox360Mode() below, but by default this XBox360 mode still always starts off DISABLED.
+                                                                 // The XBox360 always splits from the center of an edge (D3D11 uses ruler function).  Splitting
+                                     // from the center causes sliver triangles in transition areas, which cause numerous problems.
+                                  // Note the XBox360 only supports adaptive tessellation via fractional_even partitioning,
+                                  // though this #define lets you try the XBox vertex splitting order with any of the
+                                  // partitioning modes: even, odd, integer or pow2.
+#ifdef ALLOW_XBOX_360_COMPARISON
+    void SetXBox360Mode(bool bXboxMode) {m_bXBox360Mode = bXboxMode;}
+#endif
+    CHWTessellator();
+    ~CHWTessellator();
+//---------------------------------------------------------------------------------------------------------------------------------
+    //=============================================================================================================================
+    // Some defines so that numbers are usually self commenting
+    //=============================================================================================================================
+    static const int U = 0; // points on a tri patch
+    static const int V = 1;
+    static const int W = 2;
+    static const int Ueq0 = 0; // edges on a tri patch
+    static const int Veq0 = 1;
+    static const int Weq0 = 2;
+
+    static const int Ueq1 = 2; // edges on a quad patch: Ueq0, Veq0, Ueq1, Veq1
+    static const int Veq1 = 3;
+
+    static const int QUAD_AXES = 2;
+    static const int QUAD_EDGES = 4;
+    static const int TRI_EDGES = 3;
+    //=============================================================================================================================
+
+    typedef enum TESSELLATOR_PARITY // derived from D3D11_TESSELLATOR_PARTITIONING
+    {                               // (note: for integer tessellation, both parities are used)
+        TESSELLATOR_PARITY_EVEN,
+        TESSELLATOR_PARITY_ODD
+    };
+private:
+    TESSELLATOR_PARITY                   m_originalParity; // user chosen parity
+    TESSELLATOR_PARITY                   m_parity; // current parity: if allowing mix of even/odd during discrete
+                                                   // tessellation, this can vary from the user defined parity
+    D3D11_TESSELLATOR_PARTITIONING       m_originalPartitioning; // user chosen partitioning
+    D3D11_TESSELLATOR_PARTITIONING       m_partitioning; // current partitioning.  IsoLines overrides for line density
+    D3D11_TESSELLATOR_OUTPUT_PRIMITIVE   m_outputPrimitive;
+    DOMAIN_POINT*                        m_Point; // array where we will store u/v's for the points we generate
+    int*                                 m_Index; // array where we will store index topology
+    int                                  m_NumPoints;
+    int                                  m_NumIndices;
+#ifdef ALLOW_XBOX_360_COMPARISON
+    bool                                 m_bXBox360Mode;
+#endif
+    // PlacePointIn1D below is the workhorse for all position placement.
+    // It is code that could run as preamble in a Domain Shader, so the tessellator itself
+    // doesn't necessarily need to have floating point.
+    // Some per-TessFactor fixed context is needed, and that can be computed wherever
+    // the TessFactor reduction is done, perhaps as Hull Shader postamble - this is shared
+    // for all point evaluation.
+    typedef struct TESS_FACTOR_CONTEXT
+    {
+        FXP fxpInvNumSegmentsOnFloorTessFactor;
+        FXP fxpInvNumSegmentsOnCeilTessFactor;
+        FXP fxpHalfTessFactorFraction;
+        int numHalfTessFactorPoints;
+        int splitPointOnFloorHalfTessFactor;
+    } TESS_FACTOR_CONTEXT;
+    void ComputeTessFactorContext( FXP fxpTessFactor, TESS_FACTOR_CONTEXT& TessFactorCtx );
+    void PlacePointIn1D( const TESS_FACTOR_CONTEXT& TessFactorCtx, int point, FXP& fxpLocation );
+
+    int NumPointsForTessFactor(FXP fxpTessFactor);
+
+    // Tessellation parity control
+    bool Odd() {return (m_parity == TESSELLATOR_PARITY_ODD) ? true : false;}
+    void SetTessellationParity(TESSELLATOR_PARITY parity) {m_parity = parity;}
+
+    // HWIntegerPartitioning() - hardware doesn't care about what pow2 partitioning is - the query below is true for
+    //                           both integer and pow2.
+    bool HWIntegerPartitioning() {return ((m_partitioning == D3D11_TESSELLATOR_PARTITIONING_INTEGER)||
+                                          (m_partitioning == D3D11_TESSELLATOR_PARTITIONING_POW2)) ? true : false;}
+
+    // Tesselation Partitioning control
+    void RestorePartitioning() {m_partitioning = m_originalPartitioning;};
+    void OverridePartitioning(D3D11_TESSELLATOR_PARTITIONING partitioning) {m_partitioning = partitioning;} //isoline uses this for density
+
+    // Call these to generate new points and indices.  Max TessFactor storage is already allocated.
+    int DefinePoint(FXP u, FXP v, int pointStorageOffset);
+    void DefineIndex(int index, int indexStorageOffset);
+    void DefineClockwiseTriangle(int index0, int index1, int index2, int indexStorageBaseOffset);
+
+    // Couple of trivial ways to generate index data just given points and no other connectivity.
+    void DumpAllPoints();                  // Make point indices for point rendering mode -
+                                           // redundant, but just here for orthogonality.
+    void DumpAllPointsAsInOrderLineList(); // A debug visualization of all the points connected
+                                           // in the order they were generated.
+                                           // Asking to draw line topology on a tri or quad patch will do this
+
+
+    // The structures below define the data that is derived given input TessFactors and which
+    // is used by point generation and connectivity generation steps (each of which are independent)
+    typedef struct PROCESSED_TESS_FACTORS_ISOLINE
+    {
+        TESSELLATOR_PARITY lineDensityParity;
+        TESSELLATOR_PARITY lineDetailParity;
+        TESS_FACTOR_CONTEXT lineDensityTessFactorCtx;
+        TESS_FACTOR_CONTEXT lineDetailTessFactorCtx;
+        bool bPatchCulled;
+        int numPointsPerLine;
+        int numLines;
+    } PROCESSED_TESS_FACTORS_ISOLINE;
+    typedef struct PROCESSED_TESS_FACTORS_TRI
+    {
+        FXP outsideTessFactor[TRI_EDGES];
+        FXP insideTessFactor;
+        TESSELLATOR_PARITY outsideTessFactorParity[TRI_EDGES];
+        TESSELLATOR_PARITY insideTessFactorParity;
+        TESS_FACTOR_CONTEXT outsideTessFactorCtx[TRI_EDGES];
+        TESS_FACTOR_CONTEXT insideTessFactorCtx;
+        bool bJustDoMinimumTessFactor;
+        bool bPatchCulled;
+        // Stuff below is just specific to the traversal order
+        // this code happens to use to generate points/lines
+        int numPointsForOutsideEdge[TRI_EDGES];
+        int numPointsForInsideTessFactor;
+        int insideEdgePointBaseOffset;
+    } PROCESSED_TESS_FACTORS_TRI;
+    typedef struct PROCESSED_TESS_FACTORS_QUAD
+    {
+        FXP outsideTessFactor[QUAD_EDGES];
+        FXP insideTessFactor[QUAD_AXES];
+        TESSELLATOR_PARITY outsideTessFactorParity[QUAD_EDGES];
+        TESSELLATOR_PARITY insideTessFactorParity[QUAD_AXES];
+        TESS_FACTOR_CONTEXT outsideTessFactorCtx[QUAD_EDGES];
+        TESS_FACTOR_CONTEXT insideTessFactorCtx[QUAD_AXES];
+        bool bJustDoMinimumTessFactor;
+        bool bPatchCulled;
+        // Stuff below is just specific to the traversal order
+        // this code happens to use to generate points/lines
+        int numPointsForOutsideEdge[QUAD_EDGES];
+        int numPointsForInsideTessFactor[QUAD_AXES];
+        int insideEdgePointBaseOffset;
+    } PROCESSED_TESS_FACTORS_QUAD;
+
+    // These are the workhorse functions for tessellation:
+    // (1) Process input TessFactors
+    // (2) Generate points
+    // (3) Generate connectivity (can be done in parallel to (2))
+    void IsoLineProcessTessFactors( float TessFactor_V_LineDensity, float TessFactor_U_LineDetail, PROCESSED_TESS_FACTORS_ISOLINE& processedTessFactors );
+    void IsoLineGeneratePoints( const PROCESSED_TESS_FACTORS_ISOLINE& processedTessFactors );
+    void IsoLineGenerateConnectivity( const PROCESSED_TESS_FACTORS_ISOLINE& processedTessFactors );
+    void TriProcessTessFactors( float tessFactor_Ueq0, float TessFactor_Veq0, float TessFactor_Weq0, float insideTessFactor, PROCESSED_TESS_FACTORS_TRI& processedTessFactors );
+    void TriGeneratePoints( const PROCESSED_TESS_FACTORS_TRI& processedTessFactors );
+    void TriGenerateConnectivity( const PROCESSED_TESS_FACTORS_TRI& processedTessFactors );
+    void QuadProcessTessFactors( float tessFactor_Ueq0, float tessFactor_Veq0, float tessFactor_Ueq1, float tessFactor_Veq1,
+                               float insideTessFactor_U, float insideTessFactor_V, PROCESSED_TESS_FACTORS_QUAD& processedTessFactors );
+    void QuadGeneratePoints( const PROCESSED_TESS_FACTORS_QUAD& processedTessFactors );
+    void QuadGenerateConnectivity( const PROCESSED_TESS_FACTORS_QUAD& processedTessFactors );
+
+    // Stitching
+    // ---------
+    // Given pointers to the beginning of 2 parallel rows of points, and TessFactors for each, stitch them.
+    // The assumption is the stitch is symmetric.
+    void StitchTransition(int baseIndexOffset, int insideEdgePointBaseOffset, int insideNumHalfTessFactorPoints,
+                                               TESSELLATOR_PARITY insideEdgeTessFactorParity,
+                                               int outsideEdgePointBaseOffset, int outsideNumHalfTessFactorPoints,
+                                               TESSELLATOR_PARITY outsideEdgeTessFactorParity );
+    // The interior can just use a simpler stitch.
+    typedef enum DIAGONALS
+    {
+        DIAGONALS_INSIDE_TO_OUTSIDE,
+        DIAGONALS_INSIDE_TO_OUTSIDE_EXCEPT_MIDDLE,
+        DIAGONALS_MIRRORED
+    };
+
+    void StitchRegular(bool bTrapezoid, DIAGONALS diagonals, int baseIndexOffset, int numInsideEdgePoints,
+                                        int insideEdgePointBaseOffset, int outsideEdgePointBaseOffset);
+
+//---------------------------------------------------------------------------------------------------------------------------------
+    // Index Patching
+    // --------------
+    // The code below patches index values produces during triangulation, so triangulation doesn't have to know
+    // where points should go.  I happened to never produce duplicate vertices, but the patching would
+    // be simpler if some duplicate vertices were introduced in practice.  During point rendering mode however,
+    // it is not permitted for duplicate points to show up.
+
+    // Since the points are generated in concentric rings, most of the time, the point locations are
+    // sequentially increasing in memory for each side of a ring, which the stitch can take advantage of.
+    // However, there are exceptions where the points are not sequentially increasing, such as
+    // the 4th row in a given ring, where the last point on the outside of each row is actually the beginning
+    // point.
+    // So we let the stitching code think it sees sequential vertices, and when it emits a vertex index,
+    // we patch it to be the real location.
+    int  PatchIndexValue(int index);
+    typedef struct INDEX_PATCH_CONTEXT
+    {
+        int insidePointIndexDeltaToRealValue;
+        int insidePointIndexBadValue;
+        int insidePointIndexReplacementValue;
+        int outsidePointIndexPatchBase;
+        int outsidePointIndexDeltaToRealValue;
+        int outsidePointIndexBadValue;
+        int outsidePointIndexReplacementValue;
+    } INDEX_PATCH_CONTEXT;
+    void SetUsingPatchedIndices(bool bUsingPatchedIndices) {m_bUsingPatchedIndices = bUsingPatchedIndices;}
+
+    // A second index patch we have to do handles the leftover strip of quads in the middle of an odd quad patch after
+    // finishing all the concentric rings.
+    // This also handles the leftover strip of points in the middle of an even quad
+    // patch, when stitching the row of triangles up the left side (V major quad) or bottom (U major quad) of the
+    // inner ring
+    typedef struct INDEX_PATCH_CONTEXT2
+    {
+        int baseIndexToInvert;
+        int indexInversionEndPoint;
+        int cornerCaseBadValue;
+        int cornerCaseReplacementValue;
+    } INDEX_PATCH_CONTEXT2;
+    void SetUsingPatchedIndices2(bool bUsingPatchedIndices) {m_bUsingPatchedIndices2 = bUsingPatchedIndices;}
+    bool                                 m_bUsingPatchedIndices;
+    bool                                 m_bUsingPatchedIndices2;
+    INDEX_PATCH_CONTEXT                  m_IndexPatchContext;
+    INDEX_PATCH_CONTEXT2                 m_IndexPatchContext2;
+
+};
+
+//=================================================================================================================================
+// CHLSLTessellator: D3D11 Tessellation HLSL Tessellator Interface
+// Demonstrates TessFactor preconditioning code auto-generated by HLSL.  Subject to change, but this
+// just represents the effect of shader code the HLSL compiler will generate in the Hull Shader,
+// so it does not affect hardware design at all.
+//=================================================================================================================================
+class CHLSLTessellator : public CHWTessellator
+{
+public:
+    void Init( D3D11_TESSELLATOR_PARTITIONING         partitioning,
+               D3D11_TESSELLATOR_REDUCTION            insideTessFactorReduction,
+               D3D11_TESSELLATOR_QUAD_REDUCTION_AXIS  quadInsideTessFactorReductionAxis,
+               D3D11_TESSELLATOR_OUTPUT_PRIMITIVE     outputPrimitive);
+
+    void TessellateIsoLineDomain( float TessFactor_V_LineDensity,
+                                  float TessFactor_U_LineDetail );
+
+    void TessellateTriDomain( float tessFactor_Ueq0,
+                              float TessFactor_Veq0,
+                              float TessFactor_Weq0,
+                              float insideTessFactorScale /*[0..1]*/ );
+
+    void TessellateQuadDomain( float TessFactorUeq0,
+                               float TessFactorVeq0,
+                               float TessFactorUeq1,
+                               float TessFactorVeq1,
+                               float insideTessFactorScaleU /*[0..1]*/,
+                               float insideTessFactorScaleV /*[0..1]*/ );
+
+    int GetPointCount() {return CHWTessellator::GetPointCount();};
+    int GetIndexCount() {return CHWTessellator::GetIndexCount();}
+
+    DOMAIN_POINT* GetPoints() {return CHWTessellator::GetPoints();} // Get CHLSLTessellator owned pointer to vertices (UV values).
+                               // Pointer is fixed for lifetime of CHLSLTessellator object.
+    int* GetIndices() {return CHWTessellator::GetIndices();}         // Get CHLSLTessellator owned pointer to vertex indices.
+                               // Pointer is fixed for lifetime of CHLSLTessellator object.
+
+    // Retrieve TessFactors actually used by the "hardware"
+    // This includes clamping to valid range, and more interestingly
+    // if integer or pow2 partitioning is being done, the rounded TessFactors can be retrieved.
+    // Getting the rounded TessFactors can be useful for geomorphing of displacement maps.
+    float GetIsoLineDensityTessFactor() {return m_LastComputedTessFactors[0];}
+    float GetIsoLineDetailTessFactor() {return m_LastComputedTessFactors[1];}
+    float GetTriUeq0TessFactor() {return m_LastComputedTessFactors[0];}
+    float GetTriVeq0TessFactor() {return m_LastComputedTessFactors[1];}
+    float GetTriWeq0TessFactor() {return m_LastComputedTessFactors[2];}
+    float GetTriInsideTessFactor() {return m_LastComputedTessFactors[3];}
+    float GetQuadUeq0TessFactor() {return m_LastComputedTessFactors[0];}
+    float GetQuadVeq0TessFactor() {return m_LastComputedTessFactors[1];}
+    float GetQuadUeq1TessFactor() {return m_LastComputedTessFactors[2];}
+    float GetQuadVeq1TessFactor() {return m_LastComputedTessFactors[3];}
+    float GetQuadInsideUTessFactor() {return m_LastComputedTessFactors[4];}
+    float GetQuadInsideVTessFactor() {return m_LastComputedTessFactors[5];}
+    float GetUnRoundedIsoLineDensityTessFactor() {return m_LastUnRoundedComputedTessFactors[0];}
+    float GetUnRoundedIsoLineDetailTessFactor() {return m_LastUnRoundedComputedTessFactors[1];}
+    float GetUnRoundedTriUeq0TessFactor() {return m_LastUnRoundedComputedTessFactors[0];}
+    float GetUnRoundedTriVeq0TessFactor() {return m_LastUnRoundedComputedTessFactors[1];}
+    float GetUnRoundedTriWeq0TessFactor() {return m_LastUnRoundedComputedTessFactors[2];}
+    float GetUnRoundedTriInsideTessFactor() {return m_LastUnRoundedComputedTessFactors[3];}
+    float GetUnRoundedQuadUeq0TessFactor() {return m_LastUnRoundedComputedTessFactors[0];}
+    float GetUnRoundedQuadVeq0TessFactor() {return m_LastUnRoundedComputedTessFactors[1];}
+    float GetUnRoundedQuadUeq1TessFactor() {return m_LastUnRoundedComputedTessFactors[2];}
+    float GetUnRoundedQuadVeq1TessFactor() {return m_LastUnRoundedComputedTessFactors[3];}
+    float GetUnRoundedQuadInsideUTessFactor() {return m_LastUnRoundedComputedTessFactors[4];}
+    float GetUnRoundedQuadInsideVTessFactor() {return m_LastUnRoundedComputedTessFactors[5];}
+
+    CHLSLTessellator();
+//---------------------------------------------------------------------------------------------------------------------------------
+private:
+    TESSELLATOR_PARITY                   m_originalParity; // user chosen parity
+    TESSELLATOR_PARITY                   m_parity; // current parity: if allowing mix of even/odd during discrete
+                                                   // tessellation, this can vary from the user defined parity
+    D3D11_TESSELLATOR_PARTITIONING       m_originalPartitioning; // user chosen partitioning
+    D3D11_TESSELLATOR_PARTITIONING       m_partitioning; // current partitioning.  IsoLines overrides for line density
+    D3D11_TESSELLATOR_OUTPUT_PRIMITIVE   m_outputPrimitive;
+    D3D11_TESSELLATOR_REDUCTION          m_insideTessFactorReduction;
+    D3D11_TESSELLATOR_QUAD_REDUCTION_AXIS m_quadInsideTessFactorReductionAxis;
+    float                                m_LastComputedTessFactors[6]; // TessFactors used for last tessellation
+    float                                m_LastUnRoundedComputedTessFactors[6]; // TessFactors used for last tessellation (before they were rounded)
+    bool IntegerPartitioning() {return (m_partitioning == D3D11_TESSELLATOR_PARTITIONING_INTEGER) ? true : false;}
+    bool Pow2Partitioning() {return (m_partitioning == D3D11_TESSELLATOR_PARTITIONING_POW2)? true : false;}
+    void ClampTessFactor(float& TessFactor);
+    void RoundUpTessFactor(float& TessFactor);
+    void CleanupFloatTessFactor(float& input); // clamp float to [1.0f... +INF] (incl NaN->1.0f)
+    void ClampFloatTessFactorScale(float& input); // clamp float to [0.0f... +INF] (incl NaN->0.0f)
+
+    // Tessellation parity control
+    bool Odd() {return (m_parity == TESSELLATOR_PARITY_ODD) ? true : false;}
+    void SetTessellationParity(TESSELLATOR_PARITY parity) {m_parity = parity;}
+
+    // Tesselation Partitioning control
+    void RestorePartitioning() {m_partitioning = m_originalPartitioning;};
+    void OverridePartitioning(D3D11_TESSELLATOR_PARTITIONING partitioning) {m_partitioning = partitioning;} //isoline uses this for density
+
+    void IsoLineHLSLProcessTessFactors( float TessFactor_V_LineDensity, float TessFactor_U_LineDetail );
+    void TriHLSLProcessTessFactors( float tessFactor_Ueq0, float TessFactor_Veq0, float TessFactor_Weq0, float insideTessFactor );
+    void QuadHLSLProcessTessFactors( float TessFactor_Ueq0, float TessFactor_Veq0, float TessFactor_Ueq1, float TessFactor_Veq1,
+                               float insideTessFactor_U, float insideTessFactor_V );
+
+};
+