Skip to content

Commit

Permalink
[spline/bezier]Unify hardware tessellation of bezier and spline.
Browse files Browse the repository at this point in the history
  • Loading branch information
xebra committed Oct 7, 2018
1 parent 3c0fb44 commit a48a5b3
Show file tree
Hide file tree
Showing 11 changed files with 152 additions and 186 deletions.
2 changes: 1 addition & 1 deletion GPU/Common/DrawEngineCommon.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -741,7 +741,7 @@ void DrawEngineCommon::SubmitPrim(void *verts, void *inds, GEPrimitiveType prim,
}
}

void DrawEngineCommon::TessellationDataTransfer::CopyControlPoints(float *pos, float *tex, float *col, int posStride, int texStride, int colStride, const SimpleVertex *const *points, int size, u32 vertType) {
void TessellationDataTransfer::CopyControlPoints(float *pos, float *tex, float *col, int posStride, int texStride, int colStride, const SimpleVertex *const *points, int size, u32 vertType) {
bool hasColor = (vertType & GE_VTYPE_COL_MASK) != 0;
bool hasTexCoord = (vertType & GE_VTYPE_TC_MASK) != 0;

Expand Down
13 changes: 6 additions & 7 deletions GPU/Common/DrawEngineCommon.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,12 @@ inline uint32_t GetVertTypeID(uint32_t vertType, int uvGenMode) {

struct SimpleVertex;

class TessellationDataTransfer {
public:
void CopyControlPoints(float *pos, float *tex, float *col, int posStride, int texStride, int colStride, const SimpleVertex *const *points, int size, u32 vertType);
virtual void SendDataToShader(const SimpleVertex *const *points, int size, u32 vertType, const Weight2D &weights) = 0;
};

class DrawEngineCommon {
public:
DrawEngineCommon();
Expand Down Expand Up @@ -173,12 +179,5 @@ class DrawEngineCommon {

// Hardware tessellation
int numPatches;
class TessellationDataTransfer {
public:
virtual ~TessellationDataTransfer() {}
void CopyControlPoints(float *pos, float *tex, float *col, int posStride, int texStride, int colStride, const SimpleVertex *const *points, int size, u32 vertType);
// Send spline/bezier's control points to vertex shader through floating point texture.
virtual void SendDataToShader(const SimpleVertex *const *points, int size, u32 vertType, const Weight2D &weights) = 0;
};
TessellationDataTransfer *tessDataTransfer;
};
124 changes: 49 additions & 75 deletions GPU/Common/SplineCommon.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,10 @@ class Bezier3DWeight {
u32 ToKey(int tess, int count, int type) {
return tess;
}

int CalcSize(int tess, int count) {
return tess + 1;
}
};

class Spline3DWeight {
Expand Down Expand Up @@ -231,6 +235,10 @@ class Spline3DWeight {
void FromKey(u32 key, int &tess, int &count, int &type) {
tess = key & 0xFF; count = (key >> 8) & 0xFF; type = (key >> 16) & 0xFF;
}

int CalcSize(int tess, int count) {
return (count - 3) * tess + 1;
}
};

static WeightCache<Bezier3DWeight> bezierWeightsCache;
Expand All @@ -248,28 +256,6 @@ bool CanUseHardwareTessellation(GEPatchPrimType prim) {
return false;
}

// Prepare mesh of one patch for "Instanced Tessellation".
static void TessellateSplinePatchHardware(u8 *&dest, u16 *indices, int &count, const SplinePatchLocal &spatch) {
SimpleVertex *&vertices = (SimpleVertex*&)dest;

float inv_u = 1.0f / (float)spatch.tess_u;
float inv_v = 1.0f / (float)spatch.tess_v;

// Generating simple input vertices for the spline-computing vertex shader.
for (int tile_v = 0; tile_v < spatch.tess_v + 1; ++tile_v) {
for (int tile_u = 0; tile_u < spatch.tess_u + 1; ++tile_u) {
SimpleVertex &vert = vertices[tile_v * (spatch.tess_u + 1) + tile_u];
vert.pos.x = (float)tile_u;
vert.pos.y = (float)tile_v;
// For texcoord generation
vert.nrm.x = (float)tile_u * inv_u;
vert.nrm.y = (float)tile_v * inv_v;
}
}

BuildIndex(indices, count, spatch.tess_u, spatch.tess_v, spatch.primType);
}

// Tessellate single patch (4x4 control points)
template<typename T>
class Tessellator {
Expand Down Expand Up @@ -309,29 +295,6 @@ class Tessellator {
}
};

// Prepare mesh of one patch for "Instanced Tessellation".
static void TessellateBezierPatchHardware(u8 *&dest, u16 *indices, int &count, int tess_u, int tess_v, GEPatchPrimType primType) {
SimpleVertex *&vertices = (SimpleVertex*&)dest;

float inv_u = 1.0f / (float)tess_u;
float inv_v = 1.0f / (float)tess_v;

// Generating simple input vertices for the bezier-computing vertex shader.
for (int tile_v = 0; tile_v < tess_v + 1; ++tile_v) {
for (int tile_u = 0; tile_u < tess_u + 1; ++tile_u) {
SimpleVertex &vert = vertices[tile_v * (tess_u + 1) + tile_u];

vert.pos.x = (float)tile_u;
vert.pos.y = (float)tile_v;
// For texcoord generation
vert.nrm.x = (float)tile_u * inv_u;
vert.nrm.y = (float)tile_v * inv_v;
}
}

BuildIndex(indices, count, tess_u, tess_v, primType);
}

class SimpleBufferManager {
private:
u8 *buf_;
Expand Down Expand Up @@ -466,7 +429,8 @@ class SubdivisionSurface {
};

template<class Patch, class Cache>
static void SoftwareTessellation(SimpleVertex *vertices, u16 *indices, int &count, const Patch &patch, int origVertType, const SimpleVertex *const *points, SimpleBufferManager &managedBuf, Cache &weightsCache) {
static void SoftwareTessellation(SimpleVertex *vertices, u16 *indices, int &count, const Patch &patch, u32 origVertType,
const SimpleVertex *const *points, SimpleBufferManager &managedBuf, Cache &weightsCache) {
u32 key_u = weightsCache.ToKey(patch.tess_u, patch.count_u, patch.type_u);
u32 key_v = weightsCache.ToKey(patch.tess_v, patch.count_v, patch.type_v);
Weight2D weights(weightsCache, key_u, key_v);
Expand All @@ -475,8 +439,31 @@ static void SoftwareTessellation(SimpleVertex *vertices, u16 *indices, int &coun
surface.Tessellate(vertices, indices, count, origVertType);
}

// This maps GEPatchPrimType to GEPrimitiveType.
const GEPrimitiveType primType[] = { GE_PRIM_TRIANGLES, GE_PRIM_LINES, GE_PRIM_POINTS, GE_PRIM_POINTS };
template<class Patch, class Cache>
static void HardwareTessellation(SimpleVertex *vertices, u16 *indices, int &count, const Patch &patch, u32 origVertType,
const SimpleVertex *const *points, Cache &weightsCache, TessellationDataTransfer *tessDataTransfer) {
u32 key_u = weightsCache.ToKey(patch.tess_u, patch.count_u, patch.type_u);
u32 key_v = weightsCache.ToKey(patch.tess_v, patch.count_v, patch.type_v);
Weight2D weights(weightsCache, key_u, key_v);
weights.size_u = weightsCache.CalcSize(patch.tess_u, patch.count_u);
weights.size_v = weightsCache.CalcSize(patch.tess_v, patch.count_v);
tessDataTransfer->SendDataToShader(points, patch.count_u * patch.count_v, origVertType, weights);

// Generating simple input vertices for the spline-computing vertex shader.
float inv_u = 1.0f / (float)patch.tess_u;
float inv_v = 1.0f / (float)patch.tess_v;
for (int tile_v = 0; tile_v <= patch.tess_v; ++tile_v) {
for (int tile_u = 0; tile_u <= patch.tess_u; ++tile_u) {
SimpleVertex &vert = vertices[tile_v * (patch.tess_u + 1) + tile_u];
vert.pos.x = (float)tile_u;
vert.pos.y = (float)tile_v;
// For texcoord generation
vert.nrm.x = (float)tile_u * inv_u;
vert.nrm.y = (float)tile_v * inv_v;
}
}
BuildIndex(indices, count, patch.tess_u, patch.tess_v, patch.primType);
}

void DrawEngineCommon::SubmitSpline(const void *control_points, const void *indices, int tess_u, int tess_v, int count_u, int count_v, int type_u, int type_v, GEPatchPrimType prim_type, bool computeNormals, bool patchFacing, u32 vertType, int *bytesRead) {
PROFILE_THIS_SCOPE("spline");
Expand Down Expand Up @@ -536,14 +523,8 @@ void DrawEngineCommon::SubmitSpline(const void *control_points, const void *indi
patch.patchFacing = patchFacing;

if (CanUseHardwareTessellation(prim_type)) {
const u32 key_u = splineWeightsCache.ToKey(tess_u, count_u, type_u);
const u32 key_v = splineWeightsCache.ToKey(tess_v, count_v, type_v);
Weight2D weights(splineWeightsCache, key_u, key_v);
weights.size_u = (count_u - 3) * tess_u + 1;
weights.size_v = (count_v - 3) * tess_v + 1;
tessDataTransfer->SendDataToShader(points, count_u * count_v, origVertType, weights);
TessellateSplinePatchHardware(dest, quadIndices_, count, patch);
numPatches = (count_u - 3) * (count_v - 3);
HardwareTessellation((SimpleVertex *)splineBuffer, quadIndices_, count, patch, origVertType, points, splineWeightsCache, tessDataTransfer);
numPatches = patch.num_patches_u * patch.num_patches_v;
} else {
patch.Init(SPLINE_BUFFER_SIZE / vertexSize);
SoftwareTessellation((SimpleVertex *)splineBuffer, quadIndices_, count, patch, origVertType, points, managedBuf, splineWeightsCache);
Expand Down Expand Up @@ -621,27 +602,20 @@ void DrawEngineCommon::SubmitBezier(const void *control_points, const void *indi
u8 *dest = splineBuffer;
u16 *inds = quadIndices_;

// Bezier patches share less control points than spline patches. Otherwise they are pretty much the same (except bezier don't support the open/close thing)
int num_patches_u = (count_u - 1) / 3;
int num_patches_v = (count_v - 1) / 3;
BezierPatch patch;
patch.tess_u = tess_u;
patch.tess_v = tess_v;
patch.count_u = count_u;
patch.count_v = count_v;
patch.num_patches_u = (count_u - 1) / 3;
patch.num_patches_v = (count_v - 1) / 3;
patch.primType = prim_type;
patch.patchFacing = patchFacing;

if (CanUseHardwareTessellation(prim_type)) {
Weight2D weights(bezierWeightsCache, tess_u, tess_v);
weights.size_u = tess_u + 1;
weights.size_v = tess_v + 1;
tessDataTransfer->SendDataToShader(points, count_u * count_v, origVertType, weights);
TessellateBezierPatchHardware(dest, inds, count, tess_u, tess_v, prim_type);
numPatches = num_patches_u * num_patches_v;
HardwareTessellation((SimpleVertex *)splineBuffer, quadIndices_, count, patch, origVertType, points, bezierWeightsCache, tessDataTransfer);
numPatches = patch.num_patches_u * patch.num_patches_v;
} else {
BezierPatch patch;
patch.tess_u = tess_u;
patch.tess_v = tess_v;
patch.count_u = count_u;
patch.count_v = count_v;
patch.num_patches_u = (count_u - 1) / 3;
patch.num_patches_v = (count_v - 1) / 3;
patch.primType = prim_type;
patch.patchFacing = patchFacing;

patch.Init(SPLINE_BUFFER_SIZE / vertexSize);
SoftwareTessellation((SimpleVertex *)splineBuffer, quadIndices_, count, patch, origVertType, points, managedBuf, bezierWeightsCache);
}
Expand Down
55 changes: 32 additions & 23 deletions GPU/D3D11/DrawEngineD3D11.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,8 @@ void DrawEngineD3D11::InitDeviceObjects() {
pushVerts_ = new PushBufferD3D11(device_, VERTEX_PUSH_SIZE, D3D11_BIND_VERTEX_BUFFER);
pushInds_ = new PushBufferD3D11(device_, INDEX_PUSH_SIZE, D3D11_BIND_INDEX_BUFFER);

tessDataTransfer = new TessellationDataTransferD3D11(context_, device_);
tessDataTransferD3D11 = new TessellationDataTransferD3D11(context_, device_);
tessDataTransfer = tessDataTransferD3D11;
}

void DrawEngineD3D11::ClearTrackedVertexArrays() {
Expand All @@ -137,7 +138,7 @@ void DrawEngineD3D11::Resized() {
void DrawEngineD3D11::DestroyDeviceObjects() {
ClearTrackedVertexArrays();
ClearInputLayoutMap();
delete tessDataTransfer;
delete tessDataTransferD3D11;
delete pushVerts_;
delete pushInds_;
depthStencilCache_.Iterate([&](const uint64_t &key, ID3D11DepthStencilState *ds) {
Expand Down Expand Up @@ -692,7 +693,22 @@ void DrawEngineD3D11::DoFlush() {
GPUDebug::NotifyDraw();
}

void DrawEngineD3D11::TessellationDataTransferD3D11::SendDataToShader(const SimpleVertex *const *points, int size, u32 vertType, const Weight2D &weights) {
TessellationDataTransferD3D11::TessellationDataTransferD3D11(ID3D11DeviceContext *context, ID3D11Device *device)
: context_(context), device_(device) {
desc.Usage = D3D11_USAGE_DYNAMIC;
desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
desc.MiscFlags = D3D11_RESOURCE_MISC_BUFFER_STRUCTURED;
}

TessellationDataTransferD3D11::~TessellationDataTransferD3D11() {
for (int i = 0; i < 3; ++i) {
if (buf[i]) buf[i]->Release();
if (view[i]) view[i]->Release();
}
}

void TessellationDataTransferD3D11::SendDataToShader(const SimpleVertex *const *points, int size, u32 vertType, const Weight2D &weights) {
struct TessData {
float pos[3]; float pad1;
float uv[2]; float pad2[2];
Expand All @@ -701,13 +717,11 @@ void DrawEngineD3D11::TessellationDataTransferD3D11::SendDataToShader(const Simp

if (prevSize < size) {
prevSize = size;
if (buf[0]) {
buf[0]->Release();
view[0]->Release();
}
if (buf[0]) buf[0]->Release();
if (view[0]) view[0]->Release();

desc.ByteWidth = size * sizeof(TessData);
desc.StructureByteStride = sizeof(TessData);

device_->CreateBuffer(&desc, nullptr, &buf[0]);
device_->CreateShaderResourceView(buf[0], nullptr, &view[0]);
context_->VSSetShaderResources(0, 1, &view[0]);
Expand All @@ -727,17 +741,14 @@ void DrawEngineD3D11::TessellationDataTransferD3D11::SendDataToShader(const Simp

context_->Unmap(buf[0], 0);


// Weights U
if (prevSizeWeights[0] < weights.size_u) {
prevSizeWeights[0] = weights.size_u;
if (buf[1]) {
buf[1]->Release();
view[1]->Release();
}
if (prevSizeWU < weights.size_u) {
prevSizeWU = weights.size_u;
if (buf[1]) buf[1]->Release();
if (view[1]) view[1]->Release();

desc.ByteWidth = weights.size_u * sizeof(Weight);
desc.StructureByteStride = sizeof(Weight);

device_->CreateBuffer(&desc, nullptr, &buf[1]);
device_->CreateShaderResourceView(buf[1], nullptr, &view[1]);
context_->VSSetShaderResources(1, 1, &view[1]);
Expand All @@ -747,15 +758,13 @@ void DrawEngineD3D11::TessellationDataTransferD3D11::SendDataToShader(const Simp
context_->Unmap(buf[1], 0);

// Weights V
if (prevSizeWeights[1] < weights.size_v) {
prevSizeWeights[1] = weights.size_v;
if (buf[2]) {
buf[2]->Release();
view[2]->Release();
}
if (prevSizeWV < weights.size_v) {
prevSizeWV = weights.size_v;
if (buf[2]) buf[2]->Release();
if (view[2]) view[2]->Release();

desc.ByteWidth = weights.size_v * sizeof(Weight);
desc.StructureByteStride = sizeof(Weight);

device_->CreateBuffer(&desc, nullptr, &buf[2]);
device_->CreateShaderResourceView(buf[2], nullptr, &view[2]);
context_->VSSetShaderResources(2, 1, &view[2]);
Expand Down
45 changes: 17 additions & 28 deletions GPU/D3D11/DrawEngineD3D11.h
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,22 @@ class VertexArrayInfoD3D11 {
u8 flags;
};

class TessellationDataTransferD3D11 : public TessellationDataTransfer {
private:
ID3D11DeviceContext *context_;
ID3D11Device *device_;
ID3D11Buffer *buf[3]{};
ID3D11ShaderResourceView *view[3]{};
D3D11_BUFFER_DESC desc{};
int prevSize = 0;
int prevSizeWU = 0, prevSizeWV = 0;
public:
TessellationDataTransferD3D11(ID3D11DeviceContext *context, ID3D11Device *device);
~TessellationDataTransferD3D11();
// Send spline/bezier's control points and weights to vertex shader through structured shader buffer.
void SendDataToShader(const SimpleVertex *const *points, int size, u32 vertType, const Weight2D &weights) override;
};

// Handles transform, lighting and drawing.
class DrawEngineD3D11 : public DrawEngineCommon {
public:
Expand Down Expand Up @@ -199,32 +215,5 @@ class DrawEngineD3D11 : public DrawEngineCommon {
D3D11DynamicState dynState_{};

// Hardware tessellation
class TessellationDataTransferD3D11 : public TessellationDataTransfer {
private:
ID3D11DeviceContext *context_;
ID3D11Device *device_;
ID3D11Buffer *buf[3];
ID3D11ShaderResourceView *view[3];
D3D11_BUFFER_DESC desc;
int prevSize = 0;
int prevSizeWeights[2] = {};
public:
TessellationDataTransferD3D11(ID3D11DeviceContext *context, ID3D11Device *device)
: TessellationDataTransfer(), context_(context), device_(device), buf(), view(), desc() {
desc.Usage = D3D11_USAGE_DYNAMIC;
desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
desc.MiscFlags = D3D11_RESOURCE_MISC_BUFFER_STRUCTURED;
}
~TessellationDataTransferD3D11() {
for (int i = 0; i < 3; ++i) {
if (buf[i]) {
buf[i]->Release();
view[i]->Release();
}
}
}

void SendDataToShader(const SimpleVertex *const *points, int size, u32 vertType, const Weight2D &weights) override;
};
TessellationDataTransferD3D11 *tessDataTransferD3D11;
};
Loading

0 comments on commit a48a5b3

Please sign in to comment.