From 9729d8abc00dac01eed6b5c239259f3e7c259dff Mon Sep 17 00:00:00 2001 From: Magnus Norddahl Date: Wed, 11 Dec 2024 01:28:52 +0100 Subject: [PATCH] Fix median not getting initialized Added some SSE2 code that wasn't faster unfortunately --- .../hwrenderer/data/hw_collision.cpp | 267 ++++++++++++++++-- .../rendering/hwrenderer/data/hw_collision.h | 7 +- 2 files changed, 254 insertions(+), 20 deletions(-) diff --git a/src/common/rendering/hwrenderer/data/hw_collision.cpp b/src/common/rendering/hwrenderer/data/hw_collision.cpp index 17bf10cd8..a8b7e51b6 100644 --- a/src/common/rendering/hwrenderer/data/hw_collision.cpp +++ b/src/common/rendering/hwrenderer/data/hw_collision.cpp @@ -221,7 +221,7 @@ void CPUAccelStruct::CreateTLAS() for (int i = 0; i < InstanceCount; i++) { Scratch.leafs.push_back(i); - Scratch.centroids.push_back(DynamicBLAS[i]->GetBBox().Center); + Scratch.centroids.push_back(FVector4(DynamicBLAS[i]->GetBBox().Center, 1.0f)); } size_t neededbuffersize = InstanceCount * 2; @@ -232,13 +232,13 @@ void CPUAccelStruct::CreateTLAS() TLAS.Root = Subdivide(Scratch.leafs.data(), (int)Scratch.leafs.size(), Scratch.centroids.data(), Scratch.workbuffer.data()); } -int CPUAccelStruct::Subdivide(int* instances, int numInstances, const FVector3* centroids, int* workBuffer) +int CPUAccelStruct::Subdivide(int* instances, int numInstances, const FVector4* centroids, int* workBuffer) { if (numInstances == 0) return -1; // Find bounding box and median of the instance centroids - FVector3 median; + FVector3 median(0.0f, 0.0f, 0.0f); FVector3 min = DynamicBLAS[instances[0]]->GetBBox().min; FVector3 max = DynamicBLAS[instances[0]]->GetBBox().max; for (int i = 0; i < numInstances; i++) @@ -253,7 +253,7 @@ int CPUAccelStruct::Subdivide(int* instances, int numInstances, const FVector3* max.Y = std::max(max.Y, bbox.max.Y); max.Z = std::max(max.Z, bbox.max.Z); - median += centroids[instances[i]]; + median += centroids[instances[i]].XYZ(); } median /= (float)numInstances; @@ -304,7 +304,7 @@ int CPUAccelStruct::Subdivide(int* instances, int numInstances, const FVector3* { int instance = instances[i]; - float side = (FVector4(centroids[instance], 1.0f) | plane); + float side = centroids[instance] | plane; if (side >= 0.0f) { workBuffer[left_count] = instance; @@ -367,14 +367,14 @@ CPUBottomLevelAccelStruct::CPUBottomLevelAccelStruct(const FFlatVertex *vertices int element_index = i * 3; FVector3 centroid = (vertices[elements[element_index + 0]].fPos() + vertices[elements[element_index + 1]].fPos() + vertices[elements[element_index + 2]].fPos()) * (1.0f / 3.0f); - scratch.centroids.push_back(centroid); + scratch.centroids.push_back(FVector4(centroid, 1.0f)); } size_t neededbuffersize = num_triangles * 2; if (scratch.workbuffer.size() < neededbuffersize) scratch.workbuffer.resize(neededbuffersize); - root = Subdivide(&scratch.leafs[0], (int)scratch.leafs.size(), &scratch.centroids[0], scratch.workbuffer.data()); + root = Subdivide(&scratch.leafs[0], (int)scratch.leafs.size(), scratch.centroids.data(), scratch.workbuffer.data()); } TraceHit CPUBottomLevelAccelStruct::FindFirstHit(const FVector3 &ray_start, const FVector3 &ray_end) @@ -539,13 +539,204 @@ float CPUBottomLevelAccelStruct::GetBalancedDepth() const return std::log2((float)(num_elements / 3)); } -int CPUBottomLevelAccelStruct::Subdivide(int *triangles, int num_triangles, const FVector3 *centroids, int *work_buffer) +int CPUBottomLevelAccelStruct::SubdivideLeaf(int* triangles, int num_triangles) { if (num_triangles == 0) return -1; + int element_index = triangles[0] * 3; + + FVector3 min = vertices[elements[element_index]].fPos(); + FVector3 max = min; + + for (int j = 1; j < 3; j++) + { + const FVector3& vertex = vertices[elements[element_index + j]].fPos(); + + min.X = std::min(min.X, vertex.X); + min.Y = std::min(min.Y, vertex.Y); + min.Z = std::min(min.Z, vertex.Z); + + max.X = std::max(max.X, vertex.X); + max.Y = std::max(max.Y, vertex.Y); + max.Z = std::max(max.Z, vertex.Z); + } + + FVector3 margin(0.1f, 0.1f, 0.1f); + nodes.push_back(Node(min - margin, max + margin, element_index)); + return (int)nodes.size() - 1; +} + +// Sadly, this seems to be slower than what the compiler generated :( +#if 0 // #ifndef NO_SSE + +static const FVector3 axes[3] = { FVector3(-1.0f, 0.0f, 0.0f), FVector3(0.0f, -1.0f, 0.0f), FVector3(0.0f, 0.0f, -1.0f) }; + +int CPUBottomLevelAccelStruct::Subdivide(int* triangles, int num_triangles, const FVector4* centroids, int* work_buffer) +{ + if (num_triangles <= 1) + return SubdivideLeaf(triangles, num_triangles); + + // Let the compiler optimize these into registers + const FFlatVertex* vertices = this->vertices; + const unsigned int* elements = this->elements; + // Find bounding box and median of the triangle centroids - FVector3 median; + __m128 mmedian = _mm_setzero_ps(); + __m128 mmin = _mm_loadu_ps(reinterpret_cast(&vertices[elements[triangles[0] * 3]])); + __m128 mmax = mmin; + for (int i = 0; i < num_triangles; i++) + { + int v = triangles[i]; + int element_index = v + v + v; // triangles[i] * 3 + for (int j = 0; j < 3; j++) + { + __m128 vertex = _mm_loadu_ps(reinterpret_cast(&vertices[elements[element_index + j]])); + mmin = _mm_min_ps(mmin, vertex); + mmax = _mm_max_ps(mmax, vertex); + } + + mmedian = _mm_add_ps(mmedian, _mm_loadu_ps(reinterpret_cast(¢roids[triangles[i]]))); + } + mmedian = _mm_div_ps(mmedian, _mm_set1_ps((float)num_triangles)); + + // For numerical stability + mmin = _mm_sub_ps(mmin, _mm_set1_ps(0.1f)); + mmax = _mm_add_ps(mmax, _mm_set1_ps(0.1f)); + + // FFlatVertex got Y and Z swapped + mmin = _mm_shuffle_ps(mmin, mmin, _MM_SHUFFLE(3, 1, 2, 0)); + mmax = _mm_shuffle_ps(mmax, mmax, _MM_SHUFFLE(3, 1, 2, 0)); + + float min[4], max[4], median[4], axis_lengths[4]; + _mm_store_ps(min, mmin); + _mm_store_ps(max, mmax); + _mm_store_ps(median, mmedian); + _mm_store_ps(axis_lengths, _mm_sub_ps(mmax, mmin)); + + // Find the longest axis +#if 0 + int axis_order[3] = { 0, 1, 2 }; + std::sort(axis_order, axis_order + 3, [&](int a, int b) { return axis_lengths[a] > axis_lengths[b]; }); +#else + int axis_order[3]; + if (axis_lengths[0] >= axis_lengths[1] && axis_lengths[0] >= axis_lengths[2]) + { + axis_order[0] = 0; + if (axis_lengths[1] >= axis_lengths[2]) + { + axis_order[1] = 1; + axis_order[2] = 2; + } + else + { + axis_order[1] = 2; + axis_order[2] = 1; + } + } + else if (axis_lengths[1] >= axis_lengths[0] && axis_lengths[1] >= axis_lengths[2]) + { + axis_order[0] = 1; + if (axis_lengths[0] >= axis_lengths[2]) + { + axis_order[1] = 0; + axis_order[2] = 2; + } + else + { + axis_order[1] = 2; + axis_order[2] = 0; + } + } + else + { + axis_order[0] = 2; + if (axis_lengths[0] >= axis_lengths[1]) + { + axis_order[1] = 0; + axis_order[2] = 1; + } + else + { + axis_order[1] = 1; + axis_order[2] = 0; + } + } +#endif + + // Try split at longest axis, then if that fails the next longest, and then the remaining one + int left_count, right_count; + for (int attempt = 0; attempt < 3; attempt++) + { + // Find the split plane for axis + const FVector3& axis = axes[axis_order[attempt]]; + FVector4 plane(axis, median[0] * axis.X + median[1] * axis.Y + median[2] * axis.Z); // plane(axis, -dot(median, axis)); + + // Split triangles into two + left_count = 0; + right_count = 0; + for (int i = 0; i < num_triangles; i++) + { + int triangle = triangles[i]; + int element_index = triangle * 3; + + float side = centroids[triangles[i]] | plane; // dot(FVector4(centroids[triangles[i]], 1.0f), plane); + if (side >= 0.0f) + { + work_buffer[left_count] = triangle; + left_count++; + } + else + { + work_buffer[num_triangles + right_count] = triangle; + right_count++; + } + } + + if (left_count != 0 && right_count != 0) + break; + } + + // Check if something went wrong when splitting and do a random split instead + if (left_count == 0 || right_count == 0) + { + left_count = num_triangles / 2; + right_count = num_triangles - left_count; + } + else + { + // Move result back into triangles list: + for (int i = 0; i < left_count; i++) + triangles[i] = work_buffer[i]; + for (int i = 0; i < right_count; i++) + triangles[i + left_count] = work_buffer[num_triangles + i]; + } + + // Create child nodes: + int left_index = -1; + int right_index = -1; + if (left_count > 0) + left_index = Subdivide(triangles, left_count, centroids, work_buffer); + if (right_count > 0) + right_index = Subdivide(triangles + left_count, right_count, centroids, work_buffer); + + nodes.push_back(Node(FVector3(min[0], min[1], min[2]), FVector3(max[0], max[1], max[2]), left_index, right_index)); + return (int)nodes.size() - 1; +} + +#else + +int CPUBottomLevelAccelStruct::Subdivide(int *triangles, int num_triangles, const FVector4 *centroids, int *work_buffer) +{ + if (num_triangles <= 1) + return SubdivideLeaf(triangles, num_triangles); + + // Let the compiler optimize these into registers + const FFlatVertex* vertices = this->vertices; + const unsigned int* elements = this->elements; + + // Find bounding box and median of the triangle centroids + FVector3 median(0.0f, 0.0f, 0.0f); FVector3 min, max; min = vertices[elements[triangles[0] * 3]].fPos(); max = min; @@ -565,7 +756,7 @@ int CPUBottomLevelAccelStruct::Subdivide(int *triangles, int num_triangles, cons max.Z = std::max(max.Z, vertex.Z); } - median += centroids[triangles[i]]; + median += centroids[triangles[i]].XYZ(); } median /= (float)num_triangles; @@ -577,12 +768,6 @@ int CPUBottomLevelAccelStruct::Subdivide(int *triangles, int num_triangles, cons max.Y += 0.1f; max.Z += 0.1f; - if (num_triangles == 1) // Leaf node - { - nodes.push_back(Node(min, max, triangles[0] * 3)); - return (int)nodes.size() - 1; - } - // Find the longest axis float axis_lengths[3] = { @@ -591,8 +776,54 @@ int CPUBottomLevelAccelStruct::Subdivide(int *triangles, int num_triangles, cons max.Z - min.Z }; +#if 0 int axis_order[3] = { 0, 1, 2 }; std::sort(axis_order, axis_order + 3, [&](int a, int b) { return axis_lengths[a] > axis_lengths[b]; }); +#else + int axis_order[3]; + if (axis_lengths[0] >= axis_lengths[1] && axis_lengths[0] >= axis_lengths[2]) + { + axis_order[0] = 0; + if (axis_lengths[1] >= axis_lengths[2]) + { + axis_order[1] = 1; + axis_order[2] = 2; + } + else + { + axis_order[1] = 2; + axis_order[2] = 1; + } + } + else if (axis_lengths[1] >= axis_lengths[0] && axis_lengths[1] >= axis_lengths[2]) + { + axis_order[0] = 1; + if (axis_lengths[0] >= axis_lengths[2]) + { + axis_order[1] = 0; + axis_order[2] = 2; + } + else + { + axis_order[1] = 2; + axis_order[2] = 0; + } + } + else + { + axis_order[0] = 2; + if (axis_lengths[0] >= axis_lengths[1]) + { + axis_order[1] = 0; + axis_order[2] = 1; + } + else + { + axis_order[1] = 1; + axis_order[2] = 0; + } + } +#endif // Try split at longest axis, then if that fails the next longest, and then the remaining one int left_count, right_count; @@ -617,7 +848,7 @@ int CPUBottomLevelAccelStruct::Subdivide(int *triangles, int num_triangles, cons int triangle = triangles[i]; int element_index = triangle * 3; - float side = (FVector4(centroids[triangles[i]], 1.0f) | plane); // dot(FVector4(centroids[triangles[i]], 1.0f), plane); + float side = centroids[triangles[i]] | plane; // dot(FVector4(centroids[triangles[i]], 1.0f), plane); if (side >= 0.0f) { work_buffer[left_count] = triangle; @@ -661,6 +892,8 @@ int CPUBottomLevelAccelStruct::Subdivide(int *triangles, int num_triangles, cons return (int)nodes.size() - 1; } +#endif + ///////////////////////////////////////////////////////////////////////////// static const uint32_t clearsignbitmask[] = { 0x7fffffff, 0x7fffffff, 0x7fffffff, 0x7fffffff }; diff --git a/src/common/rendering/hwrenderer/data/hw_collision.h b/src/common/rendering/hwrenderer/data/hw_collision.h index a1860a6e5..db7dc6ede 100644 --- a/src/common/rendering/hwrenderer/data/hw_collision.h +++ b/src/common/rendering/hwrenderer/data/hw_collision.h @@ -94,7 +94,7 @@ class AccelStructScratchBuffer { public: std::vector leafs; - std::vector centroids; + std::vector centroids; std::vector workbuffer; }; @@ -110,7 +110,7 @@ public: private: void FindFirstHit(const RayBBox& ray, int a, TraceHit* hit); void CreateTLAS(); - int Subdivide(int* instances, int numInstances, const FVector3* centroids, int* workBuffer); + int Subdivide(int* instances, int numInstances, const FVector4* centroids, int* workBuffer); std::unique_ptr CreateBLAS(int indexStart, int indexCount); void Upload(); @@ -185,7 +185,8 @@ private: void FindFirstHit(const RayBBox& ray, int a, TraceHit* hit); float IntersectTriangleRay(const RayBBox &ray, int a, float &barycentricB, float &barycentricC); - int Subdivide(int *triangles, int num_triangles, const FVector3 *centroids, int *work_buffer); + int Subdivide(int *triangles, int num_triangles, const FVector4 *centroids, int *work_buffer); + int SubdivideLeaf(int* triangles, int num_triangles); }; class IntersectionTest