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 <airlied@redhat.com>
Acked-by: Bruce Cherniak <bruce.cherniak@intel.com>
Reviwed-by: Alok Hota <alok.hota@intel.com>
This commit is contained in:
Jan Zielinski 2019-12-04 13:10:18 +01:00
parent ff2e11b210
commit ab55708200
5 changed files with 3334 additions and 43 deletions

View File

@ -128,6 +128,8 @@ CORE_CXX_SOURCES := \
rasterizer/core/state.h \
rasterizer/core/state_funcs.h \
rasterizer/core/tessellator.h \
rasterizer/core/tessellator.hpp \
rasterizer/core/tessellator.cpp \
rasterizer/core/threads.cpp \
rasterizer/core/threads.h \
rasterizer/core/tilemgr.cpp \

View File

@ -129,6 +129,8 @@ files_swr_arch = files(
'rasterizer/core/state.h',
'rasterizer/core/state_funcs.h',
'rasterizer/core/tessellator.h',
'rasterizer/core/tessellator.hpp',
'rasterizer/core/tessellator.cpp',
'rasterizer/core/threads.cpp',
'rasterizer/core/threads.h',
'rasterizer/core/tilemgr.cpp',
@ -316,5 +318,5 @@ libmesaswr = static_library(
driver_swr = declare_dependency(
compile_args : '-DGALLIUM_SWR',
link_with : libmesaswr,
link_with : libmesaswr
)

File diff suppressed because it is too large Load Diff

View File

@ -1,9 +1,5 @@
/****************************************************************************
* Copyright (C) 2014-2015 Intel Corporation. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* Copyright (C) 2014-2019 without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
@ -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]
};
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];
NumIndices = (uint32_t)SUPER::GetIndexCount();
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
{
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;
}
/// Destroy & de-allocate tessellation context
INLINE void SWR_API TSDestroyCtx(HANDLE tsCtx) ///< [IN] Tessellation context to be destroyed
{
using Tessellator::SWR_TS;
SWR_TS* pTessellator = (SWR_TS*)tsCtx;
if (pTessellator)
{
pTessellator->~SWR_TS();
}
}
/// 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
/// @TODO - Implement OSS tessellator
INLINE HANDLE SWR_API TSInitCtx(SWR_TS_DOMAIN tsDomain,
SWR_TS_PARTITIONING tsPartitioning,
SWR_TS_OUTPUT_TOPOLOGY tsOutputTopology,
void* pContextMem,
size_t& memSize)
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;
return NULL;
}
INLINE void SWR_API TSDestroyCtx(HANDLE tsCtx)
{
SWR_NOT_IMPL;
}
INLINE void SWR_API TSTessellate(HANDLE tsCtx,
const SWR_TESSELLATION_FACTORS& tsTessFactors,
SWR_TS_TESSELLATED_DATA& tsTessellatedData)
{
SWR_NOT_IMPL;
using Tessellator::SWR_TS;
SWR_TS* pTessellator = (SWR_TS*)tsCtx;
SWR_ASSERT(pTessellator);
pTessellator->Tessellate(tsTessFactors, tsTessellatedData);
}

View File

@ -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 );
};