From ab5570820071d97c4adfe8cd8a90083f3784fa5e Mon Sep 17 00:00:00 2001 From: Jan Zielinski Date: Wed, 4 Dec 2019 13:10:18 +0100 Subject: [PATCH] swr/rasterizer: Add tessellator implementation to the rasterizer 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 Acked-by: Bruce Cherniak Reviwed-by: Alok Hota --- src/gallium/drivers/swr/Makefile.sources | 2 + src/gallium/drivers/swr/meson.build | 4 +- .../swr/rasterizer/core/tessellator.cpp | 2693 +++++++++++++++++ .../drivers/swr/rasterizer/core/tessellator.h | 191 +- .../swr/rasterizer/core/tessellator.hpp | 471 +++ 5 files changed, 3326 insertions(+), 35 deletions(-) create mode 100644 src/gallium/drivers/swr/rasterizer/core/tessellator.cpp create mode 100644 src/gallium/drivers/swr/rasterizer/core/tessellator.hpp diff --git a/src/gallium/drivers/swr/Makefile.sources b/src/gallium/drivers/swr/Makefile.sources index 720bd590d82..2f199ae348e 100644 --- a/src/gallium/drivers/swr/Makefile.sources +++ b/src/gallium/drivers/swr/Makefile.sources @@ -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 \ diff --git a/src/gallium/drivers/swr/meson.build b/src/gallium/drivers/swr/meson.build index 2fad60365a3..5a5c46dcbdd 100644 --- a/src/gallium/drivers/swr/meson.build +++ b/src/gallium/drivers/swr/meson.build @@ -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 index 00000000000..6e12b691037 --- /dev/null +++ b/src/gallium/drivers/swr/rasterizer/core/tessellator.cpp @@ -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 // ceil +#include +//#include // Just used for some commented out debug stat printing. +//#include // 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<( 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< 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); + } +} diff --git a/src/gallium/drivers/swr/rasterizer/core/tessellator.h b/src/gallium/drivers/swr/rasterizer/core/tessellator.h index 348170bfd42..04ae0237fef 100644 --- a/src/gallium/drivers/swr/rasterizer/core/tessellator.h +++ b/src/gallium/drivers/swr/rasterizer/core/tessellator.h @@ -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: @@ -27,16 +23,7 @@ ******************************************************************************/ #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 index 00000000000..77a228d8b27 --- /dev/null +++ b/src/gallium/drivers/swr/rasterizer/core/tessellator.hpp @@ -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 ); + +}; + -- 2.30.2