diff --git a/GPU/Common/SplineCommon.cpp b/GPU/Common/SplineCommon.cpp index 75866aa11b..80d9bad20c 100644 --- a/GPU/Common/SplineCommon.cpp +++ b/GPU/Common/SplineCommon.cpp @@ -343,37 +343,29 @@ void ControlPoints::Convert(const SimpleVertex *const *points, int size) { defcolor = points[0]->color_32; } -template +template class SubdivisionSurface { -private: - const ControlPoints &points; - const Patch &patch; - const Weight2D &weights; public: - SubdivisionSurface(const ControlPoints &points, const Patch &patch, const Weight2D &weights) - : points(points), patch(patch), weights(weights) { - } - template - void Tessellate(OutputBuffers &output) { - const float inv_u = 1.0f / (float)patch.tess_u; - const float inv_v = 1.0f / (float)patch.tess_v; + static void Tessellate(OutputBuffers &output, const Surface &surface, const ControlPoints &points, const Weight2D &weights) { + const float inv_u = 1.0f / (float)surface.tess_u; + const float inv_v = 1.0f / (float)surface.tess_v; - for (int patch_u = 0; patch_u < patch.num_patches_u; ++patch_u) { - const int start_u = patch.GetTessStart(patch_u); - for (int patch_v = 0; patch_v < patch.num_patches_v; ++patch_v) { - const int start_v = patch.GetTessStart(patch_v); + for (int patch_u = 0; patch_u < surface.num_patches_u; ++patch_u) { + const int start_u = surface.GetTessStart(patch_u); + for (int patch_v = 0; patch_v < surface.num_patches_v; ++patch_v) { + const int start_v = surface.GetTessStart(patch_v); // Prepare 4x4 control points to tessellate - const int idx = patch.GetPointIndex(patch_u, patch_v); - const int idx_v[4] = { idx, idx + patch.count_u, idx + patch.count_u * 2, idx + patch.count_u * 3 }; + const int idx = surface.GetPointIndex(patch_u, patch_v); + const int idx_v[4] = { idx, idx + surface.num_points_u, idx + surface.num_points_u * 2, idx + surface.num_points_u * 3 }; Tessellator tess_pos(points.pos, idx_v); Tessellator tess_col(points.col, idx_v); Tessellator tess_tex(points.tex, idx_v); Tessellator tess_nrm(points.pos, idx_v); - for (int tile_u = start_u; tile_u <= patch.tess_u; ++tile_u) { - const int index_u = patch.GetIndexU(patch_u, tile_u); + for (int tile_u = start_u; tile_u <= surface.tess_u; ++tile_u) { + const int index_u = surface.GetIndexU(patch_u, tile_u); const Weight &wu = weights.u[index_u]; // Pre-tessellate U lines @@ -385,11 +377,11 @@ public: if (sampleNrm) tess_nrm.SampleU(wu.deriv); - for (int tile_v = start_v; tile_v <= patch.tess_v; ++tile_v) { - const int index_v = patch.GetIndexV(patch_v, tile_v); + for (int tile_v = start_v; tile_v <= surface.tess_v; ++tile_v) { + const int index_v = surface.GetIndexV(patch_v, tile_v); const Weight &wv = weights.v[index_v]; - SimpleVertex &vert = output.vertices[patch.GetIndex(index_u, index_v, patch_u, patch_v)]; + SimpleVertex &vert = output.vertices[surface.GetIndex(index_u, index_v, patch_u, patch_v)]; // Tessellate vert.pos = tess_pos.SampleV(wv.basis); @@ -420,62 +412,60 @@ public: } } - patch.BuildIndex(output.indices, output.count); + surface.BuildIndex(output.indices, output.count); } - using TessFunc = void(SubdivisionSurface::*)(OutputBuffers &); + using TessFunc = void(*)(OutputBuffers &, const Surface &, const ControlPoints &, const Weight2D &); TEMPLATE_PARAMETER_DISPATCHER_FUNCTION(Tess, SubdivisionSurface::Tessellate, TessFunc); - void Tessellate(OutputBuffers &output, u32 origVertType) { - constexpr int NumParams = 5; - static TemplateParameterDispatcher dispatcher; // Initialize only once - - const bool params[NumParams] = { + static void Tessellate(OutputBuffers &output, const Surface &surface, const ControlPoints &points, const Weight2D &weights, u32 origVertType) { + const bool params[] = { (origVertType & GE_VTYPE_NRM_MASK) != 0, (origVertType & GE_VTYPE_COL_MASK) != 0, (origVertType & GE_VTYPE_TC_MASK) != 0, cpu_info.bSSE4_1, - patch.patchFacing, + surface.patchFacing, }; + static TemplateParameterDispatcher dispatcher; // Initialize only once + TessFunc func = dispatcher.GetFunc(params); - (this->*func)(output); + func(output, surface, points, weights); } }; -template -void SoftwareTessellation(OutputBuffers &output, const Patch &patch, u32 origVertType, const ControlPoints &points) { - using WeightType = typename Patch::WeightType; - u32 key_u = WeightType::ToKey(patch.tess_u, patch.count_u, patch.type_u); - u32 key_v = WeightType::ToKey(patch.tess_v, patch.count_v, patch.type_v); +template +void SoftwareTessellation(OutputBuffers &output, const Surface &surface, u32 origVertType, const ControlPoints &points) { + using WeightType = typename Surface::WeightType; + u32 key_u = WeightType::ToKey(surface.tess_u, surface.num_points_u, surface.type_u); + u32 key_v = WeightType::ToKey(surface.tess_v, surface.num_points_v, surface.type_v); Weight2D weights(WeightType::weightsCache, key_u, key_v); - SubdivisionSurface surface(points, patch, weights); - surface.Tessellate(output, origVertType); + SubdivisionSurface::Tessellate(output, surface, points, weights, origVertType); } -template -static void HardwareTessellation(OutputBuffers &output, const Patch &patch, u32 origVertType, +template +static void HardwareTessellation(OutputBuffers &output, const Surface &surface, u32 origVertType, const SimpleVertex *const *points, TessellationDataTransfer *tessDataTransfer) { - using WeightType = typename Patch::WeightType; - u32 key_u = WeightType::ToKey(patch.tess_u, patch.count_u, patch.type_u); - u32 key_v = WeightType::ToKey(patch.tess_v, patch.count_v, patch.type_v); + using WeightType = typename Surface::WeightType; + u32 key_u = WeightType::ToKey(surface.tess_u, surface.num_points_u, surface.type_u); + u32 key_v = WeightType::ToKey(surface.tess_v, surface.num_points_v, surface.type_v); Weight2D weights(WeightType::weightsCache, key_u, key_v); - weights.size_u = WeightType::CalcSize(patch.tess_u, patch.count_u); - weights.size_v = WeightType::CalcSize(patch.tess_v, patch.count_v); - tessDataTransfer->SendDataToShader(points, patch.count_u, patch.count_v, origVertType, weights); + weights.size_u = WeightType::CalcSize(surface.tess_u, surface.num_points_u); + weights.size_v = WeightType::CalcSize(surface.tess_v, surface.num_points_v); + tessDataTransfer->SendDataToShader(points, surface.num_points_u, surface.num_points_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 patch_u = 0; patch_u < patch.num_patches_u; ++patch_u) { - const int start_u = patch.GetTessStart(patch_u); - for (int patch_v = 0; patch_v < patch.num_patches_v; ++patch_v) { - const int start_v = patch.GetTessStart(patch_v); - for (int tile_u = start_u; tile_u <= patch.tess_u; ++tile_u) { - const int index_u = patch.GetIndexU(patch_u, tile_u); - for (int tile_v = start_v; tile_v <= patch.tess_v; ++tile_v) { - const int index_v = patch.GetIndexV(patch_v, tile_v); - SimpleVertex &vert = output.vertices[patch.GetIndex(index_u, index_v, patch_u, patch_v)]; + float inv_u = 1.0f / (float)surface.tess_u; + float inv_v = 1.0f / (float)surface.tess_v; + for (int patch_u = 0; patch_u < surface.num_patches_u; ++patch_u) { + const int start_u = surface.GetTessStart(patch_u); + for (int patch_v = 0; patch_v < surface.num_patches_v; ++patch_v) { + const int start_v = surface.GetTessStart(patch_v); + for (int tile_u = start_u; tile_u <= surface.tess_u; ++tile_u) { + const int index_u = surface.GetIndexU(patch_u, tile_u); + for (int tile_v = start_v; tile_v <= surface.tess_v; ++tile_v) { + const int index_v = surface.GetIndexV(patch_v, tile_v); + SimpleVertex &vert = output.vertices[surface.GetIndex(index_u, index_v, patch_u, patch_v)]; // Index for the weights vert.pos.x = index_u; vert.pos.y = index_v; @@ -489,7 +479,7 @@ static void HardwareTessellation(OutputBuffers &output, const Patch &patch, u32 } } } - patch.BuildIndex(output.indices, output.count); + surface.BuildIndex(output.indices, output.count); } 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) { @@ -525,10 +515,6 @@ void DrawEngineCommon::SubmitSpline(const void *control_points, const void *indi ERROR_LOG(G3D, "Something went really wrong, vertex size: %i vs %i", vertexSize, (int)sizeof(SimpleVertex)); } - // If specified as 0, uses 1. - if (tess_u < 1) tess_u = 1; - if (tess_v < 1) tess_v = 1; - // Make an array of pointers to the control points, to get rid of indices. const SimpleVertex **points = (const SimpleVertex **)managedBuf.Allocate(sizeof(SimpleVertex *) * count_u * count_v); for (int idx = 0; idx < count_u * count_v; idx++) @@ -539,24 +525,24 @@ void DrawEngineCommon::SubmitSpline(const void *control_points, const void *indi output.indices = quadIndices_; output.count = 0; - SplinePatchLocal patch; - patch.tess_u = tess_u; - patch.tess_v = tess_v; - patch.type_u = type_u; - patch.type_v = type_v; - patch.count_u = count_u; - patch.count_v = count_v; - patch.num_patches_u = count_u - 3; - patch.num_patches_v = count_v - 3; - patch.primType = prim_type; - patch.patchFacing = patchFacing; - patch.Init(SPLINE_BUFFER_SIZE / vertexSize); + SplineSurface surface; + surface.tess_u = tess_u; + surface.tess_v = tess_v; + surface.type_u = type_u; + surface.type_v = type_v; + surface.num_points_u = count_u; + surface.num_points_v = count_v; + surface.num_patches_u = count_u - 3; + surface.num_patches_v = count_v - 3; + surface.primType = prim_type; + surface.patchFacing = patchFacing; + surface.Init(SPLINE_BUFFER_SIZE / vertexSize); if (CanUseHardwareTessellation(prim_type)) { - HardwareTessellation(output, patch, origVertType, points, tessDataTransfer); + HardwareTessellation(output, surface, origVertType, points, tessDataTransfer); } else { ControlPoints cpoints(points, count_u * count_v, managedBuf); - SoftwareTessellation(output, patch, origVertType, cpoints); + SoftwareTessellation(output, surface, origVertType, cpoints); } u32 vertTypeWithIndex16 = (vertType & ~GE_VTYPE_IDX_MASK) | GE_VTYPE_IDX_16BIT; @@ -618,10 +604,6 @@ void DrawEngineCommon::SubmitBezier(const void *control_points, const void *indi ERROR_LOG(G3D, "Something went really wrong, vertex size: %i vs %i", vertexSize, (int)sizeof(SimpleVertex)); } - // If specified as 0, uses 1. - if (tess_u < 1) tess_u = 1; - if (tess_v < 1) tess_v = 1; - // Make an array of pointers to the control points, to get rid of indices. const SimpleVertex **points = (const SimpleVertex **)managedBuf.Allocate(sizeof(SimpleVertex *) * count_u * count_v); for (int idx = 0; idx < count_u * count_v; idx++) @@ -632,22 +614,22 @@ void DrawEngineCommon::SubmitBezier(const void *control_points, const void *indi output.indices = quadIndices_; output.count = 0; - 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); + BezierSurface surface; + surface.tess_u = tess_u; + surface.tess_v = tess_v; + surface.num_points_u = count_u; + surface.num_points_v = count_v; + surface.num_patches_u = (count_u - 1) / 3; + surface.num_patches_v = (count_v - 1) / 3; + surface.primType = prim_type; + surface.patchFacing = patchFacing; + surface.Init(SPLINE_BUFFER_SIZE / vertexSize); if (CanUseHardwareTessellation(prim_type)) { - HardwareTessellation(output, patch, origVertType, points, tessDataTransfer); + HardwareTessellation(output, surface, origVertType, points, tessDataTransfer); } else { ControlPoints cpoints(points, count_u * count_v, managedBuf); - SoftwareTessellation(output, patch, origVertType, cpoints); + SoftwareTessellation(output, surface, origVertType, cpoints); } u32 vertTypeWithIndex16 = (vertType & ~GE_VTYPE_IDX_MASK) | GE_VTYPE_IDX_16BIT; diff --git a/GPU/Common/SplineCommon.h b/GPU/Common/SplineCommon.h index 3918cf1845..fdac8b7831 100644 --- a/GPU/Common/SplineCommon.h +++ b/GPU/Common/SplineCommon.h @@ -50,21 +50,20 @@ class Spline3DWeight; // We decode all vertices into a common format for easy interpolation and stuff. // Not fast but can be optimized later. -struct BezierPatch { - using WeightType = Bezier3DWeight; - int tess_u; - int tess_v; - int count_u; - int count_v; - int type_u; - int type_v; - int num_patches_u; - int num_patches_v; +struct SurfaceInfo { + int tess_u, tess_v; + int num_points_u, num_points_v; + int num_patches_u, num_patches_v; + int type_u, type_v; GEPatchPrimType primType; bool patchFacing; - void Init(int maxVertices) { + void Init() { + // If specified as 0, uses 1. + if (tess_u < 1) tess_u = 1; + if (tess_v < 1) tess_v = 1; + switch (g_Config.iSplineBezierQuality) { case LOW_QUALITY: tess_u = 2; @@ -74,93 +73,77 @@ struct BezierPatch { // Don't cut below 2, though. if (tess_u > 2) tess_u = HALF_CEIL(tess_u); if (tess_v > 2) tess_v = HALF_CEIL(tess_v); - // Pass through - case HIGH_QUALITY: - // Downsample until it fits, in case crazy tessellation factors are sent. - while ((tess_u + 1) * (tess_v + 1) * num_patches_u * num_patches_v > maxVertices) { - tess_u--; - tess_v--; - } break; } } +}; + +struct BezierSurface : public SurfaceInfo { + using WeightType = Bezier3DWeight; + + int num_verts_per_patch; + + void Init(int maxVertices) { + SurfaceInfo::Init(); + // Downsample until it fits, in case crazy tessellation factors are sent. + while ((tess_u + 1) * (tess_v + 1) * num_patches_u * num_patches_v > maxVertices) { + tess_u--; + tess_v--; + } + num_verts_per_patch = (tess_u + 1) * (tess_v + 1); + } int GetTessStart(int patch) const { return 0; } - int GetPointIndex(int patch_u, int patch_v) const { return patch_v * 3 * count_u + patch_u * 3;} + int GetPointIndex(int patch_u, int patch_v) const { return patch_v * 3 * num_points_u + patch_u * 3; } int GetIndexU(int patch_u, int tile_u) const { return tile_u; } int GetIndexV(int patch_v, int tile_v) const { return tile_v; } - int GetPatchIndex(int patch_u, int patch_v) const { return patch_v * num_patches_u + patch_u;} int GetIndex(int index_u, int index_v, int patch_u, int patch_v) const { - return index_v * (tess_u + 1) + index_u + (tess_u + 1) * (tess_v + 1) * GetPatchIndex(patch_u, patch_v); + int patch_index = patch_v * num_patches_u + patch_u; + return index_v * (tess_u + 1) + index_u + num_verts_per_patch * patch_index; } void BuildIndex(u16 *indices, int &count) const { for (int patch_u = 0; patch_u < num_patches_u; ++patch_u) { for (int patch_v = 0; patch_v < num_patches_v; ++patch_v) { int patch_index = patch_v * num_patches_u + patch_u; - int total = patch_index * (tess_u + 1) * (tess_v + 1); + int total = patch_index * num_verts_per_patch; ::BuildIndex(indices + count, count, tess_u, tess_v, primType, total); } } } }; -struct SplinePatchLocal { +struct SplineSurface : public SurfaceInfo { using WeightType = Spline3DWeight; - int tess_u; - int tess_v; - int count_u; - int count_v; - int type_u; - int type_v; - int num_patches_u; - int num_patches_v; - int num_divisions_u; - int num_divisions_v; - bool patchFacing; - GEPatchPrimType primType; + int num_vertices_u; void Init(int maxVertices) { - switch (g_Config.iSplineBezierQuality) { - case LOW_QUALITY: - tess_u = 2; - tess_v = 2; - break; - case MEDIUM_QUALITY: - // Don't cut below 2, though. - if (tess_u > 2) tess_u = HALF_CEIL(tess_u); - if (tess_v > 2) tess_v = HALF_CEIL(tess_v); - // Pass through - case HIGH_QUALITY: - // Downsample until it fits, in case crazy tessellation factors are sent. - while ((num_patches_u * tess_u + 1) * (num_patches_v * tess_v + 1) > maxVertices) { - tess_u--; - tess_v--; - } - break; + SurfaceInfo::Init(); + // Downsample until it fits, in case crazy tessellation factors are sent. + while ((num_patches_u * tess_u + 1) * (num_patches_v * tess_v + 1) > maxVertices) { + tess_u--; + tess_v--; } - - num_divisions_u = num_patches_u * tess_u; - num_divisions_v = num_patches_v * tess_v; + num_vertices_u = num_patches_u * tess_u + 1; } int GetTessStart(int patch) const { return (patch == 0) ? 0 : 1; } - int GetPointIndex(int patch_u, int patch_v) const { return patch_v * count_u + patch_u;} + int GetPointIndex(int patch_u, int patch_v) const { return patch_v * num_points_u + patch_u; } int GetIndexU(int patch_u, int tile_u) const { return patch_u * tess_u + tile_u; } int GetIndexV(int patch_v, int tile_v) const { return patch_v * tess_v + tile_v; } int GetIndex(int index_u, int index_v, int patch_u, int patch_v) const { - return index_v * (num_divisions_u + 1) + index_u; + return index_v * num_vertices_u + index_u; } void BuildIndex(u16 *indices, int &count) const { - ::BuildIndex(indices, count, num_divisions_u, num_divisions_v, primType); + ::BuildIndex(indices, count, num_patches_u * tess_u, num_patches_v * tess_v, primType); } }; diff --git a/Windows/GEDebugger/VertexPreview.cpp b/Windows/GEDebugger/VertexPreview.cpp index c4c42f81ad..f4e54e76b8 100644 --- a/Windows/GEDebugger/VertexPreview.cpp +++ b/Windows/GEDebugger/VertexPreview.cpp @@ -171,45 +171,39 @@ static void ExpandBezier(int &count, int op, const std::vector &si if (count_u < 4 || count_v < 4) return; - int tess_u = gstate.getPatchDivisionU(); - int tess_v = gstate.getPatchDivisionV(); - // If specified as 0, uses 1. - if (tess_u < 1) tess_u = 1; - if (tess_v < 1) tess_v = 1; - - BezierPatch patch; - patch.count_u = count_u; - patch.count_v = count_v; - patch.tess_u = tess_u; - patch.tess_v = tess_v; - patch.num_patches_u = (count_u - 1) / 3; - patch.num_patches_v = (count_v - 1) / 3; - patch.primType = gstate.getPatchPrimitiveType(); - patch.patchFacing = false; + BezierSurface surface; + surface.num_points_u = count_u; + surface.num_points_v = count_v; + surface.tess_u = gstate.getPatchDivisionU(); + surface.tess_v = gstate.getPatchDivisionV(); + surface.num_patches_u = (count_u - 1) / 3; + surface.num_patches_v = (count_v - 1) / 3; + surface.primType = gstate.getPatchPrimitiveType(); + surface.patchFacing = false; + int size = count_u * count_v; // Make an array of pointers to the control points, to get rid of indices. - std::vector points(count_u * count_v); - for (int idx = 0; idx < count_u * count_v; idx++) + std::vector points(size); + for (int idx = 0; idx < size; idx++) points[idx] = simpleVerts.data() + (!indices.empty() ? indices[idx] : idx); - int total_patches = patch.num_patches_u * patch.num_patches_v; - generatedVerts.resize((tess_u + 1) * (tess_v + 1) * total_patches); - generatedInds.resize(tess_u * tess_v * 6 * total_patches); + int total_patches = surface.num_patches_u * surface.num_patches_v; + generatedVerts.resize((surface.tess_u + 1) * (surface.tess_v + 1) * total_patches); + generatedInds.resize(surface.tess_u * surface.tess_v * 6 * total_patches); OutputBuffers output; output.vertices = generatedVerts.data(); output.indices = generatedInds.data(); output.count = 0; - int size = count_u * count_v; ControlPoints cpoints; cpoints.pos = (Vec3f *)AllocateAlignedMemory(sizeof(Vec3f) * size, 16); cpoints.tex = (Vec2f *)AllocateAlignedMemory(sizeof(Vec2f) * size, 16); cpoints.col = (Vec4f *)AllocateAlignedMemory(sizeof(Vec4f) * size, 16); cpoints.Convert(points.data(), size); - patch.Init(generatedVerts.size()); - SoftwareTessellation(output, patch, gstate.vertType, cpoints); + surface.Init(generatedVerts.size()); + SoftwareTessellation(output, surface, gstate.vertType, cpoints); count = output.count; FreeAlignedMemory(cpoints.pos); @@ -224,31 +218,26 @@ static void ExpandSpline(int &count, int op, const std::vector &si if (count_u < 4 || count_v < 4) return; - int tess_u = gstate.getPatchDivisionU(); - int tess_v = gstate.getPatchDivisionV(); - // If specified as 0, uses 1. - if (tess_u < 1) tess_u = 1; - if (tess_v < 1) tess_v = 1; - - SplinePatchLocal patch; - patch.count_u = count_u; - patch.count_v = count_v; - patch.tess_u = tess_u; - patch.tess_v = tess_v; - patch.type_u = (op >> 16) & 0x3; - patch.type_v = (op >> 18) & 0x3; - patch.num_patches_u = count_u - 3; - patch.num_patches_v = count_v - 3; - patch.primType = gstate.getPatchPrimitiveType(); - patch.patchFacing = false; + SplineSurface surface; + surface.num_points_u = count_u; + surface.num_points_v = count_v; + surface.tess_u = gstate.getPatchDivisionU(); + surface.tess_v = gstate.getPatchDivisionV(); + surface.type_u = (op >> 16) & 0x3; + surface.type_v = (op >> 18) & 0x3; + surface.num_patches_u = count_u - 3; + surface.num_patches_v = count_v - 3; + surface.primType = gstate.getPatchPrimitiveType(); + surface.patchFacing = false; + int size = count_u * count_v; // Make an array of pointers to the control points, to get rid of indices. - std::vector points(count_u * count_v); - for (int idx = 0; idx < count_u * count_v; idx++) + std::vector points(size); + for (int idx = 0; idx < size; idx++) points[idx] = simpleVerts.data() + (!indices.empty() ? indices[idx] : idx); - int patch_div_s = patch.num_patches_u * patch.tess_u; - int patch_div_t = patch.num_patches_v * patch.tess_v; + int patch_div_s = surface.num_patches_u * surface.tess_u; + int patch_div_t = surface.num_patches_v * surface.tess_v; generatedVerts.resize((patch_div_s + 1) * (patch_div_t + 1)); generatedInds.resize(patch_div_s * patch_div_t * 6); @@ -257,15 +246,14 @@ static void ExpandSpline(int &count, int op, const std::vector &si output.indices = generatedInds.data(); output.count = 0; - int size = count_u * count_v; ControlPoints cpoints; cpoints.pos = (Vec3f *)AllocateAlignedMemory(sizeof(Vec3f) * size, 16); cpoints.tex = (Vec2f *)AllocateAlignedMemory(sizeof(Vec2f) * size, 16); cpoints.col = (Vec4f *)AllocateAlignedMemory(sizeof(Vec4f) * size, 16); cpoints.Convert(points.data(), size); - patch.Init(generatedVerts.size()); - SoftwareTessellation(output, patch, gstate.vertType, cpoints); + surface.Init(generatedVerts.size()); + SoftwareTessellation(output, surface, gstate.vertType, cpoints); count = output.count; FreeAlignedMemory(cpoints.pos);