From 4752db1766dad7eb2e4e3a4c31bfc2bd39432e4c Mon Sep 17 00:00:00 2001 From: Marcus Hutchings Date: Wed, 25 May 2022 12:20:55 +0100 Subject: [PATCH 01/20] Switched to linear blur to reduce performance cost in prep for realtime updates. --- rts/Game/Game.cpp | 1 + rts/Sim/Misc/SmoothHeightMesh.cpp | 195 +++++++++++++++++++++++++----- rts/Sim/Misc/SmoothHeightMesh.h | 52 ++++++++ 3 files changed, 218 insertions(+), 30 deletions(-) diff --git a/rts/Game/Game.cpp b/rts/Game/Game.cpp index cda7f4be9f..dab43d5fcc 100644 --- a/rts/Game/Game.cpp +++ b/rts/Game/Game.cpp @@ -1761,6 +1761,7 @@ void CGame::SimFrame() { helper->Update(); readMap->Update(); + smoothGround.UpdateSmoothMesh(); mapDamage->Update(); pathManager->Update(); unitHandler.Update(); diff --git a/rts/Sim/Misc/SmoothHeightMesh.cpp b/rts/Sim/Misc/SmoothHeightMesh.cpp index 0ad2ecf835..a4854ec7fd 100644 --- a/rts/Sim/Misc/SmoothHeightMesh.cpp +++ b/rts/Sim/Misc/SmoothHeightMesh.cpp @@ -41,6 +41,8 @@ static float Interpolate(float x, float y, const int maxx, const int maxy, const void SmoothHeightMesh::Init(float mx, float my, float res, float smoothRad) { + Kill(); + maxx = ((fmaxx = mx) / res) + 1; maxy = ((fmaxy = my) / res) + 1; @@ -51,7 +53,11 @@ void SmoothHeightMesh::Init(float mx, float my, float res, float smoothRad) } void SmoothHeightMesh::Kill() { - + // if (maximaHeightMap != nullptr) { + // _mm_free(maximaHeightMap); + // maximaHeightMap = nullptr; + // } + maximaMesh.clear(); mesh.clear(); origMesh.clear(); } @@ -262,25 +268,40 @@ inline static void BlurHorizontal( for_mt(0, maxy, [&](const int y) { + float avg = 0.0f; + float lv = 0; + float rv = 0; + float weight = 1.f / ((float)blurSize * 2.f); + int li = 0 - blurSize; + int ri = 1 + blurSize; + for (int x1 = li; x1 < ri; ++x1) + avg += mesh[std::max(0, std::min(maxx-1, x1)) + y * lineSize]; + for (int x = 0; x < maxx; ++x) { - float avg = 0.0f; - for (int x1 = x - blurSize; x1 <= x + blurSize; ++x1) - avg += kernel[abs(x1 - x)] * mesh[std::max(0, std::min(maxx-1, x1)) + y * lineSize]; + avg += (-lv) + rv; + // float avg = 0.0f; + // for (int x1 = x - blurSize; x1 <= x + blurSize; ++x1) + // // avg += kernel[abs(x1 - x)] * mesh[std::max(0, std::min(maxx-1, x1)) + y * lineSize]; + // avg += mesh[std::max(0, std::min(maxx-1, x1)) + y * lineSize]; const float ghaw = CGround::GetHeightReal(x * resolution, y * resolution); - smoothed[x + y * lineSize] = std::max(ghaw, avg); + smoothed[x + y * lineSize] = std::max(ghaw, avg*weight); - #pragma message ("FIX ME") - smoothed[x + y * lineSize] = std::clamp( - smoothed[x + y * lineSize], - readMap->GetCurrMinHeight(), - readMap->GetCurrMaxHeight() - ); + // #pragma message ("FIX ME") + // smoothed[x + y * lineSize] = std::clamp( + // smoothed[x + y * lineSize], + // readMap->GetCurrMinHeight(), + // readMap->GetCurrMaxHeight() + // ); - assert(smoothed[x + y * lineSize] <= readMap->GetCurrMaxHeight() ); - assert(smoothed[x + y * lineSize] >= readMap->GetCurrMinHeight() ); + // assert(smoothed[x + y * lineSize] <= readMap->GetCurrMaxHeight() ); + // assert(smoothed[x + y * lineSize] >= readMap->GetCurrMinHeight() ); + + lv = mesh[std::max(0, std::min(maxx-1, li)) + y * lineSize]; + rv = mesh[std::max(0, std::min(maxx-1, ri)) + y * lineSize]; + li++; ri++; } }); } @@ -294,29 +315,45 @@ inline static void BlurVertical( const std::vector& mesh, std::vector& smoothed ) { + //SCOPED_TIMER("Sim::SmoothHeightMesh::BlurVertical"); const int lineSize = maxx; for_mt(0, maxx, [&](const int x) { + float avg = 0.0f; + float lv = 0; + float rv = 0; + float weight = 1.f / ((float)blurSize * 2.f); + int li = 0 - blurSize; + int ri = 1 + blurSize; + for (int y1 = li; y1 < ri; ++y1) + avg += mesh[ x + std::max(0, std::min(maxy-1, y1)) * lineSize]; + for (int y = 0; y < maxy; ++y) { - float avg = 0.0f; - for (int y1 = y - blurSize; y1 <= y + blurSize; ++y1) - avg += kernel[abs(y1 - y)] * mesh[ x + std::max(0, std::min(maxy-1, y1)) * lineSize]; + avg += (-lv) + rv; + // float avg = 0.0f; + // for (int y1 = y - blurSize; y1 <= y + blurSize; ++y1) + // //avg += kernel[abs(y1 - y)] * mesh[ x + std::max(0, std::min(maxy-1, y1)) * lineSize]; + // avg += mesh[ x + std::max(0, std::min(maxy-1, y1)) * lineSize]; const float ghaw = CGround::GetHeightReal(x * resolution, y * resolution); - smoothed[x + y * lineSize] = std::max(ghaw, avg); + smoothed[x + y * lineSize] = std::max(ghaw, avg*weight); + + // #pragma message ("FIX ME") + // smoothed[x + y * lineSize] = std::clamp( + // smoothed[x + y * lineSize], + // readMap->GetCurrMinHeight(), + // readMap->GetCurrMaxHeight() + // ); - #pragma message ("FIX ME") - smoothed[x + y * lineSize] = std::clamp( - smoothed[x + y * lineSize], - readMap->GetCurrMinHeight(), - readMap->GetCurrMaxHeight() - ); + // assert(smoothed[x + y * lineSize] <= readMap->GetCurrMaxHeight() ); + // assert(smoothed[x + y * lineSize] >= readMap->GetCurrMinHeight() ); - assert(smoothed[x + y * lineSize] <= readMap->GetCurrMaxHeight() ); - assert(smoothed[x + y * lineSize] >= readMap->GetCurrMinHeight() ); + lv = mesh[ x + std::max(0, std::min(maxy-1, li)) * lineSize]; + rv = mesh[ x + std::max(0, std::min(maxy-1, ri)) * lineSize]; + li++; ri++; } }); } @@ -348,7 +385,104 @@ inline static void CheckInvariants( } } +// static int mapWidth = 0.f; + +// static float GetLocalMaxima(int2 point, int2 windowSize) { +// float localMax = -std::numeric_limits::max(); + +// auto heightMap = readMap->GetCornerHeightMapSynced(); + +// int yHalfWindow = windowSize.y / 2; +// int yStart = std::max(0, point.y - yHalfWindow); +// int yEnd = std::min(mapDims.mapy, yStart + yHalfWindow); + +// int xHalfWindow = windowSize.x / 2; +// int xStart = std::max(0, point.x - xHalfWindow); +// int xEnd = std::min(mapDims.mapx, xStart + xHalfWindow); + +// for (int y = yStart; y < yEnd; y++) { +// for (int x = xStart; x < xEnd; x++) { +// int pointIndex = x + y * mapWidth; +// localMax = std::max(localMax, heightMap[pointIndex]); +// } +// } + +// return localMax; +// } + +// static void BuildLocalMaximaGrid(std::pmr::vector &maximaHeightMap, int2 point, int2 sampleSize) { +// int windowSize = 20; + +// int yStart = std::max(0, point.y); +// int yEnd = std::min(mapDims.mapy, yStart + sampleSize.y); + +// int xStart = std::max(0, point.x); +// int xEnd = std::min(mapDims.mapx, xStart + sampleSize.x); + +// for (int y = yStart; y < yEnd; y++) { +// for (int x = xStart; x < xEnd; x++) { +// int pointIndex = x + y * mapWidth; +// maximaHeightMap[pointIndex] = GetLocalMaxima(int2(x, y), int2(windowSize, windowSize)); +// } +// } +// } + +// void SmoothHeightMesh::UpdateMapMaximaGrid() { +// constexpr int itemsPerChunk = CacheAlignedMemoryResource::cacheLineLength / sizeof(float); +// size_t chunks = chunksPerLine * mapDims.mapy; + +// for_mt(0, chunks, [&, itemsPerChunk](const int i) { +// int y = i / chunksPerLine; +// int x = i % chunksPerLine; + +// BuildLocalMaximaGrid(maximaHeightMap, int2(x, y), int2(itemsPerChunk, 1)); +// }); +// } + + +// // get built after initial changes applied +// void SmoothHeightMesh::BuildNewMapMaximaGrid() { +// constexpr int itemsPerChunk = CacheAlignedMemoryResource::cacheLineLength / sizeof(float); +// chunksPerLine = (mapDims.mapx / itemsPerChunk) + (mapDims.mapx % itemsPerChunk ? 1: 0); +// cacheAlignMapWidth = chunksPerLine * itemsPerChunk; + +// size_t mapSize = mapDims.mapy*cacheAlignMapWidth; +// maximaHeightMap.resize(mapSize); +// //maximaHeightMap = (float*)_mm_malloc(memorySize, cacheLineLength); +// //std::pmr::monotonic_buffer_resource mbr(addr, memorySize, std::pmr::null_memory_resource()); +// //maximaHeightMap = new std::pmr::vector(mapSize, &mbr); +// //std::pmr::vector maximaHeightMa(mapSize, &mbr); +// } + +void SmoothHeightMesh::UpdateSmoothMesh() +{ + if (gs->frameNum % (GAME_SPEED/2) != 0) return; + + SCOPED_TIMER("Sim::SmoothHeightMesh::UpdateSmoothMesh"); + + const int winSize = smoothRadius / resolution; + const int blurSize = std::max(1, winSize / 2); + constexpr int blurPassesCount = 1; + + FindMaximumColumnHeights(maxx, maxy, winSize, resolution, colsMaxima, maximaRows); + + for (int y = 0; y <= maxy; ++y) { + AdvanceMaximaRows(y, maxx, resolution, colsMaxima, maximaRows); + FindRadialMaximum(y, maxx, winSize, resolution, colsMaxima, maximaMesh); + FixRemainingMaxima(y, maxx, maxy, winSize, resolution, colsMaxima, maximaRows); + +#ifdef _DEBUG + CheckInvariants(y, maxx, maxy, winSize, resolution, colsMaxima, maximaRows); +#endif + } + + // actually smooth with approximate Gaussian blur passes + for (int numBlurs = blurPassesCount; numBlurs > 0; --numBlurs) { + BlurHorizontal(maxx, maxy, blurSize, resolution, gaussianKernel, (numBlurs == blurPassesCount) ? maximaMesh : mesh, tempMesh); mesh.swap(tempMesh); + BlurVertical(maxx, maxy, blurSize, resolution, gaussianKernel, mesh, tempMesh); mesh.swap(tempMesh); + } +} void SmoothHeightMesh::MakeSmoothMesh() { @@ -371,7 +505,7 @@ void SmoothHeightMesh::MakeSmoothMesh() // use sliding window of maximums to reduce computational complexity const int winSize = smoothRadius / resolution; const int blurSize = std::max(1, winSize / 2); - constexpr int blurPassesCount = 2; + constexpr int blurPassesCount = 1; const auto fillGaussianKernelFunc = [blurSize](std::vector& gaussianKernel, const float sigma) { gaussianKernel.resize(blurSize + 1); @@ -393,11 +527,12 @@ void SmoothHeightMesh::MakeSmoothMesh() }; constexpr float gSigma = 5.0f; - std::vector gaussianKernel; fillGaussianKernelFunc(gaussianKernel, gSigma); assert(mesh.empty()); + maximaMesh.resize((maxx + 1) * (maxy + 1), 0.0f); mesh.resize((maxx + 1) * (maxy + 1), 0.0f); + tempMesh.resize((maxx + 1) * (maxy + 1), 0.0f); origMesh.resize((maxx + 1) * (maxy + 1), 0.0f); colsMaxima.clear(); @@ -409,7 +544,7 @@ void SmoothHeightMesh::MakeSmoothMesh() for (int y = 0; y <= maxy; ++y) { AdvanceMaximaRows(y, maxx, resolution, colsMaxima, maximaRows); - FindRadialMaximum(y, maxx, winSize, resolution, colsMaxima, mesh); + FindRadialMaximum(y, maxx, winSize, resolution, colsMaxima, maximaMesh); FixRemainingMaxima(y, maxx, maxy, winSize, resolution, colsMaxima, maximaRows); #ifdef _DEBUG @@ -419,8 +554,8 @@ void SmoothHeightMesh::MakeSmoothMesh() // actually smooth with approximate Gaussian blur passes for (int numBlurs = blurPassesCount; numBlurs > 0; --numBlurs) { - BlurHorizontal(maxx, maxy, blurSize, resolution, gaussianKernel, mesh, origMesh); mesh.swap(origMesh); - BlurVertical(maxx, maxy, blurSize, resolution, gaussianKernel, mesh, origMesh); mesh.swap(origMesh); + BlurHorizontal(maxx, maxy, blurSize, resolution, gaussianKernel, (numBlurs == blurPassesCount) ? maximaMesh : mesh, tempMesh); mesh.swap(tempMesh); + BlurVertical(maxx, maxy, blurSize, resolution, gaussianKernel, mesh, tempMesh); mesh.swap(tempMesh); } // now contains the final smoothed heightmap, save it in origMesh diff --git a/rts/Sim/Misc/SmoothHeightMesh.h b/rts/Sim/Misc/SmoothHeightMesh.h index b183f28b00..7dd61f6ad6 100644 --- a/rts/Sim/Misc/SmoothHeightMesh.h +++ b/rts/Sim/Misc/SmoothHeightMesh.h @@ -3,16 +3,52 @@ #ifndef SMOOTH_HEIGHT_MESH_H #define SMOOTH_HEIGHT_MESH_H +#include #include class CGround; +// class CacheAlignedMemoryResource : public std::pmr::memory_resource +// { +// public: +// constexpr static int cacheLineLength = 64; + +// CacheAlignedMemoryResource() +// : std::pmr::memory_resource() +// {} + +// virtual void* +// do_allocate(size_t __bytes, size_t __alignment) { +// return _mm_malloc(__bytes, cacheLineLength); +// } + +// virtual void +// do_deallocate(void* __p, size_t __bytes, size_t __alignment) { +// _mm_free(__p); +// } + +// virtual bool +// do_is_equal(const memory_resource& __other) const noexcept { +// return (this == &__other); +// }; +// }; + +// inline std::pmr::memory_resource* +// NewCacheAlignedMemoryResource() noexcept +// { +// return new CacheAlignedMemoryResource(); +// } + /** * Provides a GetHeight(x, y) of its own that smooths the mesh. */ class SmoothHeightMesh { public: + // SmoothHeightMesh() + // : maximaHeightMap(NewCacheAlignedMemoryResource()) + // {} + void Init(float mx, float my, float res, float smoothRad); void Kill(); @@ -31,6 +67,8 @@ class SmoothHeightMesh const float* GetMeshData() const { return &mesh[0]; } const float* GetOriginalMeshData() const { return &origMesh[0]; } + void UpdateSmoothMesh(); + private: void MakeSmoothMesh(); @@ -41,11 +79,25 @@ class SmoothHeightMesh float resolution = 0.0f; float smoothRadius = 0.0f; + std::vector maximaMesh; std::vector mesh; + std::vector tempMesh; std::vector origMesh; + std::vector gaussianKernel; std::vector colsMaxima; std::vector maximaRows; + + void UpdateMapMaximaGrid(); + void BuildNewMapMaximaGrid(); + + //std::pmr::monotonic_buffer_resource maximaBuffer; + //std::pmr::polymorphic_allocator maximaAllocator; + //std::pmr::vector maximaHeightMap; + //float *maximaHeightMap = nullptr; + + //size_t cacheAlignMapWidth; + //size_t chunksPerLine; }; extern SmoothHeightMesh smoothGround; From 550e9602f7edd06d5089b36f124472d77cb05cb3 Mon Sep 17 00:00:00 2001 From: Marcus Hutchings Date: Wed, 25 May 2022 12:20:55 +0100 Subject: [PATCH 02/20] Switched to linear blur to reduce performance cost in prep for realtime updates. --- rts/Game/Game.cpp | 1 + rts/Sim/Misc/SmoothHeightMesh.cpp | 195 +++++++++++++++++++++++++----- rts/Sim/Misc/SmoothHeightMesh.h | 52 ++++++++ 3 files changed, 218 insertions(+), 30 deletions(-) diff --git a/rts/Game/Game.cpp b/rts/Game/Game.cpp index cda7f4be9f..dab43d5fcc 100644 --- a/rts/Game/Game.cpp +++ b/rts/Game/Game.cpp @@ -1761,6 +1761,7 @@ void CGame::SimFrame() { helper->Update(); readMap->Update(); + smoothGround.UpdateSmoothMesh(); mapDamage->Update(); pathManager->Update(); unitHandler.Update(); diff --git a/rts/Sim/Misc/SmoothHeightMesh.cpp b/rts/Sim/Misc/SmoothHeightMesh.cpp index 0ad2ecf835..a4854ec7fd 100644 --- a/rts/Sim/Misc/SmoothHeightMesh.cpp +++ b/rts/Sim/Misc/SmoothHeightMesh.cpp @@ -41,6 +41,8 @@ static float Interpolate(float x, float y, const int maxx, const int maxy, const void SmoothHeightMesh::Init(float mx, float my, float res, float smoothRad) { + Kill(); + maxx = ((fmaxx = mx) / res) + 1; maxy = ((fmaxy = my) / res) + 1; @@ -51,7 +53,11 @@ void SmoothHeightMesh::Init(float mx, float my, float res, float smoothRad) } void SmoothHeightMesh::Kill() { - + // if (maximaHeightMap != nullptr) { + // _mm_free(maximaHeightMap); + // maximaHeightMap = nullptr; + // } + maximaMesh.clear(); mesh.clear(); origMesh.clear(); } @@ -262,25 +268,40 @@ inline static void BlurHorizontal( for_mt(0, maxy, [&](const int y) { + float avg = 0.0f; + float lv = 0; + float rv = 0; + float weight = 1.f / ((float)blurSize * 2.f); + int li = 0 - blurSize; + int ri = 1 + blurSize; + for (int x1 = li; x1 < ri; ++x1) + avg += mesh[std::max(0, std::min(maxx-1, x1)) + y * lineSize]; + for (int x = 0; x < maxx; ++x) { - float avg = 0.0f; - for (int x1 = x - blurSize; x1 <= x + blurSize; ++x1) - avg += kernel[abs(x1 - x)] * mesh[std::max(0, std::min(maxx-1, x1)) + y * lineSize]; + avg += (-lv) + rv; + // float avg = 0.0f; + // for (int x1 = x - blurSize; x1 <= x + blurSize; ++x1) + // // avg += kernel[abs(x1 - x)] * mesh[std::max(0, std::min(maxx-1, x1)) + y * lineSize]; + // avg += mesh[std::max(0, std::min(maxx-1, x1)) + y * lineSize]; const float ghaw = CGround::GetHeightReal(x * resolution, y * resolution); - smoothed[x + y * lineSize] = std::max(ghaw, avg); + smoothed[x + y * lineSize] = std::max(ghaw, avg*weight); - #pragma message ("FIX ME") - smoothed[x + y * lineSize] = std::clamp( - smoothed[x + y * lineSize], - readMap->GetCurrMinHeight(), - readMap->GetCurrMaxHeight() - ); + // #pragma message ("FIX ME") + // smoothed[x + y * lineSize] = std::clamp( + // smoothed[x + y * lineSize], + // readMap->GetCurrMinHeight(), + // readMap->GetCurrMaxHeight() + // ); - assert(smoothed[x + y * lineSize] <= readMap->GetCurrMaxHeight() ); - assert(smoothed[x + y * lineSize] >= readMap->GetCurrMinHeight() ); + // assert(smoothed[x + y * lineSize] <= readMap->GetCurrMaxHeight() ); + // assert(smoothed[x + y * lineSize] >= readMap->GetCurrMinHeight() ); + + lv = mesh[std::max(0, std::min(maxx-1, li)) + y * lineSize]; + rv = mesh[std::max(0, std::min(maxx-1, ri)) + y * lineSize]; + li++; ri++; } }); } @@ -294,29 +315,45 @@ inline static void BlurVertical( const std::vector& mesh, std::vector& smoothed ) { + //SCOPED_TIMER("Sim::SmoothHeightMesh::BlurVertical"); const int lineSize = maxx; for_mt(0, maxx, [&](const int x) { + float avg = 0.0f; + float lv = 0; + float rv = 0; + float weight = 1.f / ((float)blurSize * 2.f); + int li = 0 - blurSize; + int ri = 1 + blurSize; + for (int y1 = li; y1 < ri; ++y1) + avg += mesh[ x + std::max(0, std::min(maxy-1, y1)) * lineSize]; + for (int y = 0; y < maxy; ++y) { - float avg = 0.0f; - for (int y1 = y - blurSize; y1 <= y + blurSize; ++y1) - avg += kernel[abs(y1 - y)] * mesh[ x + std::max(0, std::min(maxy-1, y1)) * lineSize]; + avg += (-lv) + rv; + // float avg = 0.0f; + // for (int y1 = y - blurSize; y1 <= y + blurSize; ++y1) + // //avg += kernel[abs(y1 - y)] * mesh[ x + std::max(0, std::min(maxy-1, y1)) * lineSize]; + // avg += mesh[ x + std::max(0, std::min(maxy-1, y1)) * lineSize]; const float ghaw = CGround::GetHeightReal(x * resolution, y * resolution); - smoothed[x + y * lineSize] = std::max(ghaw, avg); + smoothed[x + y * lineSize] = std::max(ghaw, avg*weight); + + // #pragma message ("FIX ME") + // smoothed[x + y * lineSize] = std::clamp( + // smoothed[x + y * lineSize], + // readMap->GetCurrMinHeight(), + // readMap->GetCurrMaxHeight() + // ); - #pragma message ("FIX ME") - smoothed[x + y * lineSize] = std::clamp( - smoothed[x + y * lineSize], - readMap->GetCurrMinHeight(), - readMap->GetCurrMaxHeight() - ); + // assert(smoothed[x + y * lineSize] <= readMap->GetCurrMaxHeight() ); + // assert(smoothed[x + y * lineSize] >= readMap->GetCurrMinHeight() ); - assert(smoothed[x + y * lineSize] <= readMap->GetCurrMaxHeight() ); - assert(smoothed[x + y * lineSize] >= readMap->GetCurrMinHeight() ); + lv = mesh[ x + std::max(0, std::min(maxy-1, li)) * lineSize]; + rv = mesh[ x + std::max(0, std::min(maxy-1, ri)) * lineSize]; + li++; ri++; } }); } @@ -348,7 +385,104 @@ inline static void CheckInvariants( } } +// static int mapWidth = 0.f; + +// static float GetLocalMaxima(int2 point, int2 windowSize) { +// float localMax = -std::numeric_limits::max(); + +// auto heightMap = readMap->GetCornerHeightMapSynced(); + +// int yHalfWindow = windowSize.y / 2; +// int yStart = std::max(0, point.y - yHalfWindow); +// int yEnd = std::min(mapDims.mapy, yStart + yHalfWindow); + +// int xHalfWindow = windowSize.x / 2; +// int xStart = std::max(0, point.x - xHalfWindow); +// int xEnd = std::min(mapDims.mapx, xStart + xHalfWindow); + +// for (int y = yStart; y < yEnd; y++) { +// for (int x = xStart; x < xEnd; x++) { +// int pointIndex = x + y * mapWidth; +// localMax = std::max(localMax, heightMap[pointIndex]); +// } +// } + +// return localMax; +// } + +// static void BuildLocalMaximaGrid(std::pmr::vector &maximaHeightMap, int2 point, int2 sampleSize) { +// int windowSize = 20; + +// int yStart = std::max(0, point.y); +// int yEnd = std::min(mapDims.mapy, yStart + sampleSize.y); + +// int xStart = std::max(0, point.x); +// int xEnd = std::min(mapDims.mapx, xStart + sampleSize.x); + +// for (int y = yStart; y < yEnd; y++) { +// for (int x = xStart; x < xEnd; x++) { +// int pointIndex = x + y * mapWidth; +// maximaHeightMap[pointIndex] = GetLocalMaxima(int2(x, y), int2(windowSize, windowSize)); +// } +// } +// } + +// void SmoothHeightMesh::UpdateMapMaximaGrid() { +// constexpr int itemsPerChunk = CacheAlignedMemoryResource::cacheLineLength / sizeof(float); +// size_t chunks = chunksPerLine * mapDims.mapy; + +// for_mt(0, chunks, [&, itemsPerChunk](const int i) { +// int y = i / chunksPerLine; +// int x = i % chunksPerLine; + +// BuildLocalMaximaGrid(maximaHeightMap, int2(x, y), int2(itemsPerChunk, 1)); +// }); +// } + + +// // get built after initial changes applied +// void SmoothHeightMesh::BuildNewMapMaximaGrid() { +// constexpr int itemsPerChunk = CacheAlignedMemoryResource::cacheLineLength / sizeof(float); +// chunksPerLine = (mapDims.mapx / itemsPerChunk) + (mapDims.mapx % itemsPerChunk ? 1: 0); +// cacheAlignMapWidth = chunksPerLine * itemsPerChunk; + +// size_t mapSize = mapDims.mapy*cacheAlignMapWidth; +// maximaHeightMap.resize(mapSize); +// //maximaHeightMap = (float*)_mm_malloc(memorySize, cacheLineLength); +// //std::pmr::monotonic_buffer_resource mbr(addr, memorySize, std::pmr::null_memory_resource()); +// //maximaHeightMap = new std::pmr::vector(mapSize, &mbr); +// //std::pmr::vector maximaHeightMa(mapSize, &mbr); +// } + +void SmoothHeightMesh::UpdateSmoothMesh() +{ + if (gs->frameNum % (GAME_SPEED/2) != 0) return; + + SCOPED_TIMER("Sim::SmoothHeightMesh::UpdateSmoothMesh"); + + const int winSize = smoothRadius / resolution; + const int blurSize = std::max(1, winSize / 2); + constexpr int blurPassesCount = 1; + + FindMaximumColumnHeights(maxx, maxy, winSize, resolution, colsMaxima, maximaRows); + + for (int y = 0; y <= maxy; ++y) { + AdvanceMaximaRows(y, maxx, resolution, colsMaxima, maximaRows); + FindRadialMaximum(y, maxx, winSize, resolution, colsMaxima, maximaMesh); + FixRemainingMaxima(y, maxx, maxy, winSize, resolution, colsMaxima, maximaRows); + +#ifdef _DEBUG + CheckInvariants(y, maxx, maxy, winSize, resolution, colsMaxima, maximaRows); +#endif + } + + // actually smooth with approximate Gaussian blur passes + for (int numBlurs = blurPassesCount; numBlurs > 0; --numBlurs) { + BlurHorizontal(maxx, maxy, blurSize, resolution, gaussianKernel, (numBlurs == blurPassesCount) ? maximaMesh : mesh, tempMesh); mesh.swap(tempMesh); + BlurVertical(maxx, maxy, blurSize, resolution, gaussianKernel, mesh, tempMesh); mesh.swap(tempMesh); + } +} void SmoothHeightMesh::MakeSmoothMesh() { @@ -371,7 +505,7 @@ void SmoothHeightMesh::MakeSmoothMesh() // use sliding window of maximums to reduce computational complexity const int winSize = smoothRadius / resolution; const int blurSize = std::max(1, winSize / 2); - constexpr int blurPassesCount = 2; + constexpr int blurPassesCount = 1; const auto fillGaussianKernelFunc = [blurSize](std::vector& gaussianKernel, const float sigma) { gaussianKernel.resize(blurSize + 1); @@ -393,11 +527,12 @@ void SmoothHeightMesh::MakeSmoothMesh() }; constexpr float gSigma = 5.0f; - std::vector gaussianKernel; fillGaussianKernelFunc(gaussianKernel, gSigma); assert(mesh.empty()); + maximaMesh.resize((maxx + 1) * (maxy + 1), 0.0f); mesh.resize((maxx + 1) * (maxy + 1), 0.0f); + tempMesh.resize((maxx + 1) * (maxy + 1), 0.0f); origMesh.resize((maxx + 1) * (maxy + 1), 0.0f); colsMaxima.clear(); @@ -409,7 +544,7 @@ void SmoothHeightMesh::MakeSmoothMesh() for (int y = 0; y <= maxy; ++y) { AdvanceMaximaRows(y, maxx, resolution, colsMaxima, maximaRows); - FindRadialMaximum(y, maxx, winSize, resolution, colsMaxima, mesh); + FindRadialMaximum(y, maxx, winSize, resolution, colsMaxima, maximaMesh); FixRemainingMaxima(y, maxx, maxy, winSize, resolution, colsMaxima, maximaRows); #ifdef _DEBUG @@ -419,8 +554,8 @@ void SmoothHeightMesh::MakeSmoothMesh() // actually smooth with approximate Gaussian blur passes for (int numBlurs = blurPassesCount; numBlurs > 0; --numBlurs) { - BlurHorizontal(maxx, maxy, blurSize, resolution, gaussianKernel, mesh, origMesh); mesh.swap(origMesh); - BlurVertical(maxx, maxy, blurSize, resolution, gaussianKernel, mesh, origMesh); mesh.swap(origMesh); + BlurHorizontal(maxx, maxy, blurSize, resolution, gaussianKernel, (numBlurs == blurPassesCount) ? maximaMesh : mesh, tempMesh); mesh.swap(tempMesh); + BlurVertical(maxx, maxy, blurSize, resolution, gaussianKernel, mesh, tempMesh); mesh.swap(tempMesh); } // now contains the final smoothed heightmap, save it in origMesh diff --git a/rts/Sim/Misc/SmoothHeightMesh.h b/rts/Sim/Misc/SmoothHeightMesh.h index b183f28b00..7dd61f6ad6 100644 --- a/rts/Sim/Misc/SmoothHeightMesh.h +++ b/rts/Sim/Misc/SmoothHeightMesh.h @@ -3,16 +3,52 @@ #ifndef SMOOTH_HEIGHT_MESH_H #define SMOOTH_HEIGHT_MESH_H +#include #include class CGround; +// class CacheAlignedMemoryResource : public std::pmr::memory_resource +// { +// public: +// constexpr static int cacheLineLength = 64; + +// CacheAlignedMemoryResource() +// : std::pmr::memory_resource() +// {} + +// virtual void* +// do_allocate(size_t __bytes, size_t __alignment) { +// return _mm_malloc(__bytes, cacheLineLength); +// } + +// virtual void +// do_deallocate(void* __p, size_t __bytes, size_t __alignment) { +// _mm_free(__p); +// } + +// virtual bool +// do_is_equal(const memory_resource& __other) const noexcept { +// return (this == &__other); +// }; +// }; + +// inline std::pmr::memory_resource* +// NewCacheAlignedMemoryResource() noexcept +// { +// return new CacheAlignedMemoryResource(); +// } + /** * Provides a GetHeight(x, y) of its own that smooths the mesh. */ class SmoothHeightMesh { public: + // SmoothHeightMesh() + // : maximaHeightMap(NewCacheAlignedMemoryResource()) + // {} + void Init(float mx, float my, float res, float smoothRad); void Kill(); @@ -31,6 +67,8 @@ class SmoothHeightMesh const float* GetMeshData() const { return &mesh[0]; } const float* GetOriginalMeshData() const { return &origMesh[0]; } + void UpdateSmoothMesh(); + private: void MakeSmoothMesh(); @@ -41,11 +79,25 @@ class SmoothHeightMesh float resolution = 0.0f; float smoothRadius = 0.0f; + std::vector maximaMesh; std::vector mesh; + std::vector tempMesh; std::vector origMesh; + std::vector gaussianKernel; std::vector colsMaxima; std::vector maximaRows; + + void UpdateMapMaximaGrid(); + void BuildNewMapMaximaGrid(); + + //std::pmr::monotonic_buffer_resource maximaBuffer; + //std::pmr::polymorphic_allocator maximaAllocator; + //std::pmr::vector maximaHeightMap; + //float *maximaHeightMap = nullptr; + + //size_t cacheAlignMapWidth; + //size_t chunksPerLine; }; extern SmoothHeightMesh smoothGround; From da143b2e453d754716ad54d53119fbfb1e3c41f2 Mon Sep 17 00:00:00 2001 From: Marcus Hutchings Date: Thu, 26 May 2022 11:58:16 +0100 Subject: [PATCH 03/20] Smooth mesh generation can operate on ranges rather than the whole map. --- rts/Sim/Misc/SmoothHeightMesh.cpp | 113 +++++++++++++++++------------- 1 file changed, 63 insertions(+), 50 deletions(-) diff --git a/rts/Sim/Misc/SmoothHeightMesh.cpp b/rts/Sim/Misc/SmoothHeightMesh.cpp index a4854ec7fd..ca46ff0386 100644 --- a/rts/Sim/Misc/SmoothHeightMesh.cpp +++ b/rts/Sim/Misc/SmoothHeightMesh.cpp @@ -96,8 +96,8 @@ float SmoothHeightMesh::SetMaxHeight(int index, float h) inline static void FindMaximumColumnHeights( - const int maxx, - const int maxy, + const int2 min, + const int2 max, const int winSize, const float resolution, std::vector& colsMaxima, @@ -105,8 +105,8 @@ inline static void FindMaximumColumnHeights( ) { // initialize the algorithm: find the maximum // height per column and the corresponding row - for (int y = 0; y <= std::min(maxy, winSize); ++y) { - for (int x = 0; x <= maxx; ++x) { + for (int y = min.y; y <= std::min(max.y, min.y + winSize); ++y) { + for (int x = min.x; x <= max.x; ++x) { const float curx = x * resolution; const float cury = y * resolution; const float curh = CGround::GetHeightReal(curx, cury); @@ -121,6 +121,7 @@ inline static void FindMaximumColumnHeights( inline static void AdvanceMaximaRows( const int y, + const int minx, const int maxx, const float resolution, const std::vector& colsMaxima, @@ -129,7 +130,7 @@ inline static void AdvanceMaximaRows( const float cury = y * resolution; // try to advance rows if they're equal to current maximum but are further away - for (int x = 0; x <= maxx; ++x) { + for (int x = minx; x <= maxx; ++x) { if (maximaRows[x] == (y - 1)) { const float curx = x * resolution; const float curh = CGround::GetHeightReal(curx, cury); @@ -147,6 +148,7 @@ inline static void AdvanceMaximaRows( inline static void FindRadialMaximum( int y, + int minx, int maxx, int winSize, float resolution, @@ -155,7 +157,7 @@ inline static void FindRadialMaximum( ) { const float cury = y * resolution; - for (int x = 0; x < maxx; ++x) { + for (int x = minx; x < maxx; ++x) { float maxRowHeight = -std::numeric_limits::max(); // find current maximum within radius smoothRadius @@ -197,31 +199,30 @@ inline static void FindRadialMaximum( inline static void FixRemainingMaxima( - const int y, - const int maxx, - const int maxy, + const int2 min, + const int2 max, const int winSize, const float resolution, std::vector& colsMaxima, std::vector& maximaRows ) { // fix remaining maximums after a pass - const int nextrow = y + winSize + 1; + const int nextrow = min.y + winSize + 1; const float nextrowy = nextrow * resolution; - for (int x = 0; x <= maxx; ++x) { + for (int x = 0; x <= max.x; ++x) { #ifdef _DEBUG - for (int y1 = std::max(0, y - winSize); y1 <= std::min(maxy, y + winSize); ++y1) { + for (int y1 = std::max(min.y, min.y - winSize); y1 <= std::min(max.y, min.y + winSize); ++y1) { assert(CGround::GetHeightReal(x * resolution, y1 * resolution) <= colsMaxima[x]); } #endif const float curx = x * resolution; - if (maximaRows[x] <= (y - winSize)) { + if (maximaRows[x] <= (min.y - winSize)) { // find a new maximum if the old one left the window colsMaxima[x] = -std::numeric_limits::max(); - for (int y1 = std::max(0, y - winSize + 1); y1 <= std::min(maxy, nextrow); ++y1) { + for (int y1 = std::max(0, min.y - winSize + 1); y1 <= std::min(max.y, nextrow); ++y1) { const float h = CGround::GetHeightReal(curx, y1 * resolution); if (h > colsMaxima[x]) { @@ -232,7 +233,7 @@ inline static void FixRemainingMaxima( maximaRows[x] = y1; } } - } else if (nextrow <= maxy) { + } else if (nextrow <= max.y) { // else, just check if a new maximum has entered the window const float h = CGround::GetHeightReal(curx, nextrowy); @@ -243,10 +244,10 @@ inline static void FixRemainingMaxima( } assert(maximaRows[x] <= nextrow); - assert(maximaRows[x] >= y - winSize + 1); + assert(maximaRows[x] >= min.y - winSize + 1); #ifdef _DEBUG - for (int y1 = std::max(0, y - winSize + 1); y1 <= std::min(maxy, y + winSize + 1); ++y1) { + for (int y1 = std::max(0, min.y - winSize + 1); y1 <= std::min(max.y, min.y + winSize + 1); ++y1) { assert(colsMaxima[x] >= CGround::GetHeightReal(curx, y1 * resolution)); } #endif @@ -256,28 +257,29 @@ inline static void FixRemainingMaxima( inline static void BlurHorizontal( - const int maxx, - const int maxy, + const int2 mapSize, + const int2 min, + const int2 max, const int blurSize, const float resolution, const std::vector& kernel, const std::vector& mesh, std::vector& smoothed ) { - const int lineSize = maxx; + const int lineSize = mapSize.x; - for_mt(0, maxy, [&](const int y) + for_mt(min.y, max.y, [&](const int y) { float avg = 0.0f; float lv = 0; float rv = 0; float weight = 1.f / ((float)blurSize * 2.f); - int li = 0 - blurSize; - int ri = 1 + blurSize; + int li = min.x - blurSize; + int ri = min.x + blurSize + 1; for (int x1 = li; x1 < ri; ++x1) - avg += mesh[std::max(0, std::min(maxx-1, x1)) + y * lineSize]; + avg += mesh[std::max(0, std::min(mapSize.x -1, x1)) + y * lineSize]; - for (int x = 0; x < maxx; ++x) + for (int x = min.x; x < max.x; ++x) { avg += (-lv) + rv; // float avg = 0.0f; @@ -299,16 +301,17 @@ inline static void BlurHorizontal( // assert(smoothed[x + y * lineSize] <= readMap->GetCurrMaxHeight() ); // assert(smoothed[x + y * lineSize] >= readMap->GetCurrMinHeight() ); - lv = mesh[std::max(0, std::min(maxx-1, li)) + y * lineSize]; - rv = mesh[std::max(0, std::min(maxx-1, ri)) + y * lineSize]; + lv = mesh[std::max(0, std::min(mapSize.x-1, li)) + y * lineSize]; + rv = mesh[std::max(0, std::min(mapSize.x-1, ri)) + y * lineSize]; li++; ri++; } }); } inline static void BlurVertical( - const int maxx, - const int maxy, + const int2 mapSize, + const int2 min, + const int2 max, const int blurSize, const float resolution, const std::vector& kernel, @@ -316,20 +319,20 @@ inline static void BlurVertical( std::vector& smoothed ) { //SCOPED_TIMER("Sim::SmoothHeightMesh::BlurVertical"); - const int lineSize = maxx; + const int lineSize = mapSize.x; - for_mt(0, maxx, [&](const int x) + for_mt(min.x, max.x, [&](const int x) { float avg = 0.0f; float lv = 0; float rv = 0; float weight = 1.f / ((float)blurSize * 2.f); - int li = 0 - blurSize; - int ri = 1 + blurSize; + int li = min.y - blurSize; + int ri = min.y + blurSize + 1; for (int y1 = li; y1 < ri; ++y1) - avg += mesh[ x + std::max(0, std::min(maxy-1, y1)) * lineSize]; + avg += mesh[ x + std::max(0, std::min(mapSize.y-1, y1)) * lineSize]; - for (int y = 0; y < maxy; ++y) + for (int y = min.y; y < max.y; ++y) { avg += (-lv) + rv; // float avg = 0.0f; @@ -351,8 +354,8 @@ inline static void BlurVertical( // assert(smoothed[x + y * lineSize] <= readMap->GetCurrMaxHeight() ); // assert(smoothed[x + y * lineSize] >= readMap->GetCurrMinHeight() ); - lv = mesh[ x + std::max(0, std::min(maxy-1, li)) * lineSize]; - rv = mesh[ x + std::max(0, std::min(maxy-1, ri)) * lineSize]; + lv = mesh[ x + std::max(0, std::min(mapSize.y-1, li)) * lineSize]; + rv = mesh[ x + std::max(0, std::min(mapSize.y-1, ri)) * lineSize]; li++; ri++; } }); @@ -465,22 +468,28 @@ void SmoothHeightMesh::UpdateSmoothMesh() const int blurSize = std::max(1, winSize / 2); constexpr int blurPassesCount = 1; - FindMaximumColumnHeights(maxx, maxy, winSize, resolution, colsMaxima, maximaRows); + int2 updateLocation{200, 200}; + int2 updateLimit = updateLocation + int2{winSize*2, winSize*2}; + int2 min{0, 0}; + int2 max{maxx, maxy}; + int2 map{maxx, maxy}; + + FindMaximumColumnHeights(updateLocation, updateLimit, winSize, resolution, colsMaxima, maximaRows); for (int y = 0; y <= maxy; ++y) { - AdvanceMaximaRows(y, maxx, resolution, colsMaxima, maximaRows); - FindRadialMaximum(y, maxx, winSize, resolution, colsMaxima, maximaMesh); - FixRemainingMaxima(y, maxx, maxy, winSize, resolution, colsMaxima, maximaRows); + AdvanceMaximaRows(y, updateLocation.x, updateLimit.x, resolution, colsMaxima, maximaRows); + FindRadialMaximum(y, updateLocation.x, updateLimit.x, winSize, resolution, colsMaxima, maximaMesh); + FixRemainingMaxima(updateLocation, updateLimit, winSize, resolution, colsMaxima, maximaRows); #ifdef _DEBUG - CheckInvariants(y, maxx, maxy, winSize, resolution, colsMaxima, maximaRows); + CheckInvariants(y, updateLimit.x, updateLimit.y, winSize, resolution, colsMaxima, maximaRows); #endif } // actually smooth with approximate Gaussian blur passes for (int numBlurs = blurPassesCount; numBlurs > 0; --numBlurs) { - BlurHorizontal(maxx, maxy, blurSize, resolution, gaussianKernel, (numBlurs == blurPassesCount) ? maximaMesh : mesh, tempMesh); mesh.swap(tempMesh); - BlurVertical(maxx, maxy, blurSize, resolution, gaussianKernel, mesh, tempMesh); mesh.swap(tempMesh); + BlurHorizontal(map, updateLocation, updateLimit, blurSize, resolution, gaussianKernel, (numBlurs == blurPassesCount) ? maximaMesh : mesh, tempMesh); mesh.swap(tempMesh); + BlurVertical(map, updateLocation, updateLimit, blurSize, resolution, gaussianKernel, mesh, tempMesh); mesh.swap(tempMesh); } } @@ -540,12 +549,15 @@ void SmoothHeightMesh::MakeSmoothMesh() maximaRows.clear(); maximaRows.resize(maxx + 1, -1); - FindMaximumColumnHeights(maxx, maxy, winSize, resolution, colsMaxima, maximaRows); + int2 min{0, 0}; + int2 max{maxx, maxy}; + + FindMaximumColumnHeights(int2{0, 0}, int2{maxx, maxy}, winSize, resolution, colsMaxima, maximaRows); for (int y = 0; y <= maxy; ++y) { - AdvanceMaximaRows(y, maxx, resolution, colsMaxima, maximaRows); - FindRadialMaximum(y, maxx, winSize, resolution, colsMaxima, maximaMesh); - FixRemainingMaxima(y, maxx, maxy, winSize, resolution, colsMaxima, maximaRows); + AdvanceMaximaRows(y, 0, maxx, resolution, colsMaxima, maximaRows); + FindRadialMaximum(y, 0, maxx, winSize, resolution, colsMaxima, maximaMesh); + FixRemainingMaxima(int2{0, y}, int2{maxx, maxy}, winSize, resolution, colsMaxima, maximaRows); #ifdef _DEBUG CheckInvariants(y, maxx, maxy, winSize, resolution, colsMaxima, maximaRows); @@ -554,8 +566,9 @@ void SmoothHeightMesh::MakeSmoothMesh() // actually smooth with approximate Gaussian blur passes for (int numBlurs = blurPassesCount; numBlurs > 0; --numBlurs) { - BlurHorizontal(maxx, maxy, blurSize, resolution, gaussianKernel, (numBlurs == blurPassesCount) ? maximaMesh : mesh, tempMesh); mesh.swap(tempMesh); - BlurVertical(maxx, maxy, blurSize, resolution, gaussianKernel, mesh, tempMesh); mesh.swap(tempMesh); + + BlurHorizontal(max, min, max, blurSize, resolution, gaussianKernel, (numBlurs == blurPassesCount) ? maximaMesh : mesh, tempMesh); mesh.swap(tempMesh); + BlurVertical(max, min, max, blurSize, resolution, gaussianKernel, mesh, tempMesh); mesh.swap(tempMesh); } // now contains the final smoothed heightmap, save it in origMesh From bb26e04eaa307f2171e796eb2a0fa583d84bbca6 Mon Sep 17 00:00:00 2001 From: Marcus Hutchings Date: Thu, 26 May 2022 19:54:11 +0100 Subject: [PATCH 04/20] addressed issue where wider area was updated than was supposed to be --- rts/Sim/Misc/SmoothHeightMesh.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/rts/Sim/Misc/SmoothHeightMesh.cpp b/rts/Sim/Misc/SmoothHeightMesh.cpp index ca46ff0386..05d2c9e7be 100644 --- a/rts/Sim/Misc/SmoothHeightMesh.cpp +++ b/rts/Sim/Misc/SmoothHeightMesh.cpp @@ -166,9 +166,9 @@ inline static void FindRadialMaximum( const int endx = std::min(maxx - 1, x + winSize); for (int i = startx; i <= endx; ++i) { - assert(i >= 0); - assert(i <= maxx); - assert(CGround::GetHeightReal(i * resolution, cury) <= colsMaxima[i]); + // assert(i >= 0); + // assert(i <= maxx); + // assert(CGround::GetHeightReal(i * resolution, cury) <= colsMaxima[i]); maxRowHeight = std::max(colsMaxima[i], maxRowHeight); } @@ -210,7 +210,7 @@ inline static void FixRemainingMaxima( const int nextrow = min.y + winSize + 1; const float nextrowy = nextrow * resolution; - for (int x = 0; x <= max.x; ++x) { + for (int x = min.x; x <= max.x; ++x) { #ifdef _DEBUG for (int y1 = std::max(min.y, min.y - winSize); y1 <= std::min(max.y, min.y + winSize); ++y1) { assert(CGround::GetHeightReal(x * resolution, y1 * resolution) <= colsMaxima[x]); @@ -566,11 +566,12 @@ void SmoothHeightMesh::MakeSmoothMesh() // actually smooth with approximate Gaussian blur passes for (int numBlurs = blurPassesCount; numBlurs > 0; --numBlurs) { - BlurHorizontal(max, min, max, blurSize, resolution, gaussianKernel, (numBlurs == blurPassesCount) ? maximaMesh : mesh, tempMesh); mesh.swap(tempMesh); BlurVertical(max, min, max, blurSize, resolution, gaussianKernel, mesh, tempMesh); mesh.swap(tempMesh); } // now contains the final smoothed heightmap, save it in origMesh std::copy(mesh.begin(), mesh.end(), origMesh.begin()); + //std::copy(maximaMesh.begin(), maximaMesh.end(), origMesh.begin()); + //std::copy(maximaMesh.begin(), maximaMesh.end(), mesh.begin()); } From 08fd4552ac314df00d9249d9ee42e7552a8d7d0d Mon Sep 17 00:00:00 2001 From: Marcus Hutchings Date: Fri, 27 May 2022 15:09:58 +0100 Subject: [PATCH 05/20] realigned smooth mesh over heightmap --- rts/Game/Game.cpp | 4 +- rts/Sim/Misc/SmoothHeightMesh.cpp | 276 +++++++++++++++++------------- rts/Sim/Misc/SmoothHeightMesh.h | 11 +- 3 files changed, 163 insertions(+), 128 deletions(-) diff --git a/rts/Game/Game.cpp b/rts/Game/Game.cpp index dab43d5fcc..0ce0c751f8 100644 --- a/rts/Game/Game.cpp +++ b/rts/Game/Game.cpp @@ -574,7 +574,9 @@ void CGame::PreLoadSimulation(LuaParser* defsParser) ENTER_SYNCED_CODE(); loadscreen->SetLoadMessage("Creating Smooth Height Mesh"); - smoothGround.Init(float3::maxxpos, float3::maxzpos, SQUARE_SIZE * 2, SQUARE_SIZE * 40); + smoothGround.Init (/*float3::maxxpos, float3::maxzpos*/int2(mapDims.mapx, mapDims.mapy) + , /*SQUARE_SIZE **/ 2 + , /*SQUARE_SIZE **/ 40); loadscreen->SetLoadMessage("Creating QuadField & CEGs"); moveDefHandler.Init(defsParser); diff --git a/rts/Sim/Misc/SmoothHeightMesh.cpp b/rts/Sim/Misc/SmoothHeightMesh.cpp index 05d2c9e7be..254ed2105f 100644 --- a/rts/Sim/Misc/SmoothHeightMesh.cpp +++ b/rts/Sim/Misc/SmoothHeightMesh.cpp @@ -18,15 +18,15 @@ SmoothHeightMesh smoothGround; static float Interpolate(float x, float y, const int maxx, const int maxy, const float res, const float* heightmap) { - x = Clamp(x / res, 0.0f, maxx - 1.0f); - y = Clamp(y / res, 0.0f, maxy - 1.0f); + x = Clamp(x / res, 0.0f, (float)maxx); + y = Clamp(y / res, 0.0f, (float)maxy); const int sx = x; const int sy = y; const float dx = (x - sx); const float dy = (y - sy); - const int sxp1 = std::min(sx + 1, maxx - 1); - const int syp1 = std::min(sy + 1, maxy - 1); + const int sxp1 = std::min(sx + 1, maxx); + const int syp1 = std::min(sy + 1, maxy); const float& h1 = heightmap[sx + sy * maxx]; const float& h2 = heightmap[sxp1 + sy * maxx]; @@ -39,15 +39,19 @@ static float Interpolate(float x, float y, const int maxx, const int maxy, const } -void SmoothHeightMesh::Init(float mx, float my, float res, float smoothRad) +void SmoothHeightMesh::Init(int2 max, int res, int smoothRad) { Kill(); - maxx = ((fmaxx = mx) / res) + 1; - maxy = ((fmaxy = my) / res) + 1; + fmaxx = max.x * SQUARE_SIZE; + fmaxy = max.y * SQUARE_SIZE; + fresolution = res * SQUARE_SIZE; resolution = res; - smoothRadius = std::max(1.0f, smoothRad); + maxx = max.x / resolution; // max.x; + maxy = max.y / resolution; // max.y; + + smoothRadius = std::max(1, smoothRad); MakeSmoothMesh(); } @@ -67,7 +71,7 @@ void SmoothHeightMesh::Kill() { float SmoothHeightMesh::GetHeight(float x, float y) { assert(!mesh.empty()); - return Interpolate(x, y, maxx, maxy, resolution, &mesh[0]); + return Interpolate(x, y, maxx, maxy, fresolution, &mesh[0]); } float SmoothHeightMesh::GetHeightAboveWater(float x, float y) @@ -99,7 +103,7 @@ inline static void FindMaximumColumnHeights( const int2 min, const int2 max, const int winSize, - const float resolution, + const int resolution, std::vector& colsMaxima, std::vector& maximaRows ) { @@ -107,9 +111,10 @@ inline static void FindMaximumColumnHeights( // height per column and the corresponding row for (int y = min.y; y <= std::min(max.y, min.y + winSize); ++y) { for (int x = min.x; x <= max.x; ++x) { - const float curx = x * resolution; - const float cury = y * resolution; - const float curh = CGround::GetHeightReal(curx, cury); + //const float curx = x * resolution; + //const float cury = y * resolution; + //const float curh = CGround::GetHeightReal(curx, cury); + const float curh = readMap->GetCornerHeightMapSynced()[(x + y*mapDims.mapxp1)*resolution]; if (curh > colsMaxima[x]) { colsMaxima[x] = curh; @@ -123,17 +128,18 @@ inline static void AdvanceMaximaRows( const int y, const int minx, const int maxx, - const float resolution, + const int resolution, const std::vector& colsMaxima, std::vector& maximaRows ) { - const float cury = y * resolution; + //const float cury = y * resolution; // try to advance rows if they're equal to current maximum but are further away for (int x = minx; x <= maxx; ++x) { if (maximaRows[x] == (y - 1)) { - const float curx = x * resolution; - const float curh = CGround::GetHeightReal(curx, cury); + //const float curx = x * resolution; + //const float curh = CGround::GetHeightReal(curx, cury); + const float curh = readMap->GetCornerHeightMapSynced()[(x + y*mapDims.mapxp1)*resolution]; if (curh == colsMaxima[x]) { maximaRows[x] = y; @@ -147,6 +153,7 @@ inline static void AdvanceMaximaRows( inline static void FindRadialMaximum( + int mapx, int y, int minx, int maxx, @@ -155,15 +162,15 @@ inline static void FindRadialMaximum( const std::vector& colsMaxima, std::vector& mesh ) { - const float cury = y * resolution; + //const float cury = y * resolution; - for (int x = minx; x < maxx; ++x) { + for (int x = minx; x <= maxx; ++x) { float maxRowHeight = -std::numeric_limits::max(); // find current maximum within radius smoothRadius // (in every column stack) along the current row const int startx = std::max(x - winSize, 0); - const int endx = std::min(maxx - 1, x + winSize); + const int endx = std::min(maxx, x + winSize); for (int i = startx; i <= endx; ++i) { // assert(i >= 0); @@ -173,57 +180,59 @@ inline static void FindRadialMaximum( maxRowHeight = std::max(colsMaxima[i], maxRowHeight); } -#ifndef NDEBUG - const float curx = x * resolution; - assert(maxRowHeight <= readMap->GetCurrMaxHeight()); - assert(maxRowHeight >= CGround::GetHeightReal(curx, cury)); +// #ifndef NDEBUG +// const float curx = x * resolution; +// assert(maxRowHeight <= readMap->GetCurrMaxHeight()); +// assert(maxRowHeight >= CGround::GetHeightReal(curx, cury)); - #ifdef SMOOTHMESH_CORRECTNESS_CHECK - // naive algorithm - float maxRowHeightAlt = -std::numeric_limits::max(); +// #ifdef SMOOTHMESH_CORRECTNESS_CHECK +// // naive algorithm +// float maxRowHeightAlt = -std::numeric_limits::max(); - for (float y1 = cury - smoothRadius; y1 <= cury + smoothRadius; y1 += resolution) { - for (float x1 = curx - smoothRadius; x1 <= curx + smoothRadius; x1 += resolution) { - maxRowHeightAlt = std::max(maxRowHeightAlt, CGround::GetHeightReal(x1, y1)); - } - } +// for (float y1 = cury - smoothRadius; y1 <= cury + smoothRadius; y1 += resolution) { +// for (float x1 = curx - smoothRadius; x1 <= curx + smoothRadius; x1 += resolution) { +// maxRowHeightAlt = std::max(maxRowHeightAlt, CGround::GetHeightReal(x1, y1)); +// } +// } - assert(maxRowHeightAlt == maxRowHeight); - #endif -#endif +// assert(maxRowHeightAlt == maxRowHeight); +// #endif +// #endif - mesh[x + y * maxx] = maxRowHeight; + mesh[x + y * mapx] = maxRowHeight; } } inline static void FixRemainingMaxima( - const int2 min, + const int minx, + const int miny, const int2 max, const int winSize, - const float resolution, + const int resolution, std::vector& colsMaxima, std::vector& maximaRows ) { // fix remaining maximums after a pass - const int nextrow = min.y + winSize + 1; - const float nextrowy = nextrow * resolution; + const int nextrow = miny + winSize + 1; + const int nextrowy = nextrow; // * resolution; - for (int x = min.x; x <= max.x; ++x) { -#ifdef _DEBUG - for (int y1 = std::max(min.y, min.y - winSize); y1 <= std::min(max.y, min.y + winSize); ++y1) { - assert(CGround::GetHeightReal(x * resolution, y1 * resolution) <= colsMaxima[x]); - } -#endif - const float curx = x * resolution; + for (int x = minx; x <= max.x; ++x) { +// #ifdef _DEBUG +// for (int y1 = std::max(min.y, min.y - winSize); y1 <= std::min(max.y, min.y + winSize); ++y1) { +// assert(CGround::GetHeightReal(x * resolution, y1 * resolution) <= colsMaxima[x]); +// } +// #endif + //const int curx = x * resolution; - if (maximaRows[x] <= (min.y - winSize)) { + if (maximaRows[x] <= (miny - winSize)) { // find a new maximum if the old one left the window colsMaxima[x] = -std::numeric_limits::max(); - for (int y1 = std::max(0, min.y - winSize + 1); y1 <= std::min(max.y, nextrow); ++y1) { - const float h = CGround::GetHeightReal(curx, y1 * resolution); + for (int y1 = std::max(0, miny - winSize + 1); y1 <= std::min(max.y, nextrow); ++y1) { + //const float h = CGround::GetHeightReal(curx, y1 * resolution); + const float h = readMap->GetCornerHeightMapSynced()[(x + y1*mapDims.mapxp1)*resolution]; if (h > colsMaxima[x]) { colsMaxima[x] = h; @@ -235,7 +244,8 @@ inline static void FixRemainingMaxima( } } else if (nextrow <= max.y) { // else, just check if a new maximum has entered the window - const float h = CGround::GetHeightReal(curx, nextrowy); + //const float h = CGround::GetHeightReal(curx, nextrowy); + const float h = readMap->GetCornerHeightMapSynced()[(x + nextrowy*mapDims.mapxp1)*resolution]; if (h > colsMaxima[x]) { colsMaxima[x] = h; @@ -243,14 +253,14 @@ inline static void FixRemainingMaxima( } } - assert(maximaRows[x] <= nextrow); - assert(maximaRows[x] >= min.y - winSize + 1); + // assert(maximaRows[x] <= nextrow); + // assert(maximaRows[x] >= min.y - winSize + 1); -#ifdef _DEBUG - for (int y1 = std::max(0, min.y - winSize + 1); y1 <= std::min(max.y, min.y + winSize + 1); ++y1) { - assert(colsMaxima[x] >= CGround::GetHeightReal(curx, y1 * resolution)); - } -#endif +// #ifdef _DEBUG +// for (int y1 = std::max(0, min.y - winSize + 1); y1 <= std::min(max.y, min.y + winSize + 1); ++y1) { +// assert(colsMaxima[x] >= CGround::GetHeightReal(curx, y1 * resolution)); +// } +// #endif } } @@ -261,33 +271,30 @@ inline static void BlurHorizontal( const int2 min, const int2 max, const int blurSize, - const float resolution, + const int resolution, const std::vector& kernel, const std::vector& mesh, std::vector& smoothed ) { const int lineSize = mapSize.x; - for_mt(min.y, max.y, [&](const int y) + for_mt(min.y, max.y+1, [&](const int y) { float avg = 0.0f; float lv = 0; float rv = 0; - float weight = 1.f / ((float)blurSize * 2.f); + float weight = 1.f / ((float)blurSize * 2.f + 1.f); int li = min.x - blurSize; int ri = min.x + blurSize + 1; for (int x1 = li; x1 < ri; ++x1) - avg += mesh[std::max(0, std::min(mapSize.x -1, x1)) + y * lineSize]; + avg += mesh[std::max(0, std::min(mapSize.x-1, x1)) + y * lineSize]; for (int x = min.x; x < max.x; ++x) { avg += (-lv) + rv; - // float avg = 0.0f; - // for (int x1 = x - blurSize; x1 <= x + blurSize; ++x1) - // // avg += kernel[abs(x1 - x)] * mesh[std::max(0, std::min(maxx-1, x1)) + y * lineSize]; - // avg += mesh[std::max(0, std::min(maxx-1, x1)) + y * lineSize]; - const float ghaw = CGround::GetHeightReal(x * resolution, y * resolution); + //const float ghaw = CGround::GetHeightReal(x * resolution, y * resolution); + const float ghaw = readMap->GetCornerHeightMapSynced()[(x + y*mapDims.mapxp1)*resolution]; smoothed[x + y * lineSize] = std::max(ghaw, avg*weight); @@ -313,7 +320,7 @@ inline static void BlurVertical( const int2 min, const int2 max, const int blurSize, - const float resolution, + const int resolution, const std::vector& kernel, const std::vector& mesh, std::vector& smoothed @@ -321,12 +328,12 @@ inline static void BlurVertical( //SCOPED_TIMER("Sim::SmoothHeightMesh::BlurVertical"); const int lineSize = mapSize.x; - for_mt(min.x, max.x, [&](const int x) + for_mt(min.x, max.x+1, [&](const int x) { float avg = 0.0f; float lv = 0; float rv = 0; - float weight = 1.f / ((float)blurSize * 2.f); + float weight = 1.f / ((float)blurSize * 2.f + 1.f); int li = min.y - blurSize; int ri = min.y + blurSize + 1; for (int y1 = li; y1 < ri; ++y1) @@ -335,12 +342,9 @@ inline static void BlurVertical( for (int y = min.y; y < max.y; ++y) { avg += (-lv) + rv; - // float avg = 0.0f; - // for (int y1 = y - blurSize; y1 <= y + blurSize; ++y1) - // //avg += kernel[abs(y1 - y)] * mesh[ x + std::max(0, std::min(maxy-1, y1)) * lineSize]; - // avg += mesh[ x + std::max(0, std::min(maxy-1, y1)) * lineSize]; - const float ghaw = CGround::GetHeightReal(x * resolution, y * resolution); + //const float ghaw = CGround::GetHeightReal(x * resolution, y * resolution); + const float ghaw = readMap->GetCornerHeightMapSynced()[(x + y*mapDims.mapxp1)*resolution]; smoothed[x + y * lineSize] = std::max(ghaw, avg*weight); @@ -468,22 +472,41 @@ void SmoothHeightMesh::UpdateSmoothMesh() const int blurSize = std::max(1, winSize / 2); constexpr int blurPassesCount = 1; - int2 updateLocation{200, 200}; - int2 updateLimit = updateLocation + int2{winSize*2, winSize*2}; - int2 min{0, 0}; - int2 max{maxx, maxy}; + int2 impactRadius{winSize, winSize}; + + // area of map damaged. + int2 damageMin{40,40}; + int2 damageMax{44,44}; + + // area of the map which may need a max height change + int2 updateLocation = damageMin - impactRadius; + int2 updateLimit = damageMax + impactRadius; + + // area of map to load maximums for to update the updateLocation + int2 min = updateLocation - impactRadius; + int2 max = updateLocation + impactRadius; int2 map{maxx, maxy}; - FindMaximumColumnHeights(updateLocation, updateLimit, winSize, resolution, colsMaxima, maximaRows); + updateLocation.x = std::clamp(updateLocation.x, 0, map.x); + updateLocation.y = std::clamp(updateLocation.y, 0, map.y); + updateLimit.x = std::clamp(updateLimit.x, 0, map.x); + updateLimit.y = std::clamp(updateLimit.y, 0, map.y); + min.x = std::clamp(min.x, 0, map.x); + min.y = std::clamp(min.y, 0, map.y); + max.x = std::clamp(max.x, 0, map.x); + max.y = std::clamp(max.y, 0, map.y); - for (int y = 0; y <= maxy; ++y) { - AdvanceMaximaRows(y, updateLocation.x, updateLimit.x, resolution, colsMaxima, maximaRows); - FindRadialMaximum(y, updateLocation.x, updateLimit.x, winSize, resolution, colsMaxima, maximaMesh); - FixRemainingMaxima(updateLocation, updateLimit, winSize, resolution, colsMaxima, maximaRows); + FindMaximumColumnHeights(min, max, winSize, resolution, colsMaxima, maximaRows); -#ifdef _DEBUG - CheckInvariants(y, updateLimit.x, updateLimit.y, winSize, resolution, colsMaxima, maximaRows); -#endif + for (int y = updateLocation.y; y <= updateLimit.y; ++y) { + + AdvanceMaximaRows(y, min.x, max.x, resolution, colsMaxima, maximaRows); + FindRadialMaximum(map.x, y, updateLocation.x, updateLimit.x, winSize, resolution, colsMaxima, maximaMesh); + FixRemainingMaxima(min.x, y, max, winSize, resolution, colsMaxima, maximaRows); + +// #ifdef _DEBUG +// CheckInvariants(y, updateLimit.x, updateLimit.y, winSize, resolution, colsMaxima, maximaRows); +// #endif } // actually smooth with approximate Gaussian blur passes @@ -516,62 +539,69 @@ void SmoothHeightMesh::MakeSmoothMesh() const int blurSize = std::max(1, winSize / 2); constexpr int blurPassesCount = 1; - const auto fillGaussianKernelFunc = [blurSize](std::vector& gaussianKernel, const float sigma) { - gaussianKernel.resize(blurSize + 1); + // const auto fillGaussianKernelFunc = [blurSize](std::vector& gaussianKernel, const float sigma) { + // gaussianKernel.resize(blurSize + 1); - const auto gaussianG = [](const int x, const float sigma) -> float { - // 0.3989422804f = 1/sqrt(2*pi) - return 0.3989422804f * math::expf(-0.5f * x * x / (sigma * sigma)) / sigma; - }; + // const auto gaussianG = [](const int x, const float sigma) -> float { + // // 0.3989422804f = 1/sqrt(2*pi) + // return 0.3989422804f * math::expf(-0.5f * x * x / (sigma * sigma)) / sigma; + // }; - float sum = (gaussianKernel[0] = gaussianG(0, sigma)); + // float sum = (gaussianKernel[0] = gaussianG(0, sigma)); - for (int i = 1; i < blurSize + 1; ++i) { - sum += 2.0f * (gaussianKernel[i] = gaussianG(i, sigma)); - } + // for (int i = 1; i < blurSize + 1; ++i) { + // sum += 2.0f * (gaussianKernel[i] = gaussianG(i, sigma)); + // } - for (auto& gk : gaussianKernel) { - gk /= sum; - } - }; + // for (auto& gk : gaussianKernel) { + // gk /= sum; + // } + // }; - constexpr float gSigma = 5.0f; - fillGaussianKernelFunc(gaussianKernel, gSigma); + // constexpr float gSigma = 5.0f; + // fillGaussianKernelFunc(gaussianKernel, gSigma); assert(mesh.empty()); - maximaMesh.resize((maxx + 1) * (maxy + 1), 0.0f); - mesh.resize((maxx + 1) * (maxy + 1), 0.0f); - tempMesh.resize((maxx + 1) * (maxy + 1), 0.0f); - origMesh.resize((maxx + 1) * (maxy + 1), 0.0f); + maximaMesh.resize((maxx) * (maxy), 0.0f); + mesh.resize((maxx) * (maxy), 0.0f); + tempMesh.resize((maxx) * (maxy), 0.0f); + origMesh.resize((maxx) * (maxy), 0.0f); colsMaxima.clear(); - colsMaxima.resize(maxx + 1, -std::numeric_limits::max()); + colsMaxima.resize(maxx, -std::numeric_limits::max()); maximaRows.clear(); - maximaRows.resize(maxx + 1, -1); + maximaRows.resize(maxx, -1); int2 min{0, 0}; - int2 max{maxx, maxy}; + int2 max{maxx-1, maxy-1}; + int2 map{maxx, maxy}; - FindMaximumColumnHeights(int2{0, 0}, int2{maxx, maxy}, winSize, resolution, colsMaxima, maximaRows); +// FindMaximumColumnHeights(int2{0, 0}, max, winSize, resolution, colsMaxima, maximaRows); - for (int y = 0; y <= maxy; ++y) { - AdvanceMaximaRows(y, 0, maxx, resolution, colsMaxima, maximaRows); - FindRadialMaximum(y, 0, maxx, winSize, resolution, colsMaxima, maximaMesh); - FixRemainingMaxima(int2{0, y}, int2{maxx, maxy}, winSize, resolution, colsMaxima, maximaRows); +// for (int y = 0; y <= max.y; ++y) { +// AdvanceMaximaRows(y, 0, max.x, resolution, colsMaxima, maximaRows); +// FindRadialMaximum(map.x, y, 0, max.x, winSize, resolution, colsMaxima, maximaMesh); +// FixRemainingMaxima(0, y, max, winSize, resolution, colsMaxima, maximaRows); -#ifdef _DEBUG - CheckInvariants(y, maxx, maxy, winSize, resolution, colsMaxima, maximaRows); -#endif - } +// // #ifdef _DEBUG +// // CheckInvariants(y, maxx, maxy, winSize, resolution, colsMaxima, maximaRows); +// // #endif +// } // actually smooth with approximate Gaussian blur passes for (int numBlurs = blurPassesCount; numBlurs > 0; --numBlurs) { - BlurHorizontal(max, min, max, blurSize, resolution, gaussianKernel, (numBlurs == blurPassesCount) ? maximaMesh : mesh, tempMesh); mesh.swap(tempMesh); - BlurVertical(max, min, max, blurSize, resolution, gaussianKernel, mesh, tempMesh); mesh.swap(tempMesh); + BlurHorizontal(map, min, max, blurSize, resolution, gaussianKernel, (numBlurs == blurPassesCount) ? maximaMesh : mesh, tempMesh); mesh.swap(tempMesh); + BlurVertical(map, min, max, blurSize, resolution, gaussianKernel, mesh, tempMesh); mesh.swap(tempMesh); } + // int i = 0; + // for (int y =0; y < maxy; y++) + // for (int x = 0; x < maxx; x++) { + // maximaMesh[i++] = readMap->GetCornerHeightMapSynced()[(x + y*mapDims.mapxp1)*resolution]; + // } + // now contains the final smoothed heightmap, save it in origMesh std::copy(mesh.begin(), mesh.end(), origMesh.begin()); - //std::copy(maximaMesh.begin(), maximaMesh.end(), origMesh.begin()); - //std::copy(maximaMesh.begin(), maximaMesh.end(), mesh.begin()); + // std::copy(maximaMesh.begin(), maximaMesh.end(), origMesh.begin()); + // std::copy(maximaMesh.begin(), maximaMesh.end(), mesh.begin()); } diff --git a/rts/Sim/Misc/SmoothHeightMesh.h b/rts/Sim/Misc/SmoothHeightMesh.h index 7dd61f6ad6..caa621bca3 100644 --- a/rts/Sim/Misc/SmoothHeightMesh.h +++ b/rts/Sim/Misc/SmoothHeightMesh.h @@ -6,6 +6,8 @@ #include #include +#include "System/type2.h" + class CGround; // class CacheAlignedMemoryResource : public std::pmr::memory_resource @@ -49,7 +51,7 @@ class SmoothHeightMesh // : maximaHeightMap(NewCacheAlignedMemoryResource()) // {} - void Init(float mx, float my, float res, float smoothRad); + void Init(int2 max, int res, int smoothRad); void Kill(); float GetHeight(float x, float y); @@ -62,7 +64,7 @@ class SmoothHeightMesh int GetMaxY() const { return maxy; } float GetFMaxX() const { return fmaxx; } float GetFMaxY() const { return fmaxy; } - float GetResolution() const { return resolution; } + float GetResolution() const { return fresolution; } const float* GetMeshData() const { return &mesh[0]; } const float* GetOriginalMeshData() const { return &origMesh[0]; } @@ -76,8 +78,9 @@ class SmoothHeightMesh int maxy = 0; float fmaxx = 0.0f; float fmaxy = 0.0f; - float resolution = 0.0f; - float smoothRadius = 0.0f; + float fresolution = 0.f; + int resolution = 0; + int smoothRadius = 0; std::vector maximaMesh; std::vector mesh; From ad68c0245981fa571b2b22c44265f90c93ab7fdd Mon Sep 17 00:00:00 2001 From: Marcus Hutchings Date: Sat, 28 May 2022 08:35:41 +0100 Subject: [PATCH 06/20] Further optimisations. --- rts/Sim/Misc/SmoothHeightMesh.cpp | 205 ++++++++++++------------------ rts/Sim/Misc/SmoothHeightMesh.h | 43 ------- 2 files changed, 78 insertions(+), 170 deletions(-) diff --git a/rts/Sim/Misc/SmoothHeightMesh.cpp b/rts/Sim/Misc/SmoothHeightMesh.cpp index 254ed2105f..a5a12ff6b7 100644 --- a/rts/Sim/Misc/SmoothHeightMesh.cpp +++ b/rts/Sim/Misc/SmoothHeightMesh.cpp @@ -13,6 +13,8 @@ #include "System/TimeProfiler.h" #include "System/Threading/ThreadPool.h" +#include "System/Log/ILog.h" + SmoothHeightMesh smoothGround; @@ -48,8 +50,8 @@ void SmoothHeightMesh::Init(int2 max, int res, int smoothRad) fresolution = res * SQUARE_SIZE; resolution = res; - maxx = max.x / resolution; // max.x; - maxy = max.y / resolution; // max.y; + maxx = max.x / resolution; + maxy = max.y / resolution; smoothRadius = std::max(1, smoothRad); @@ -57,10 +59,6 @@ void SmoothHeightMesh::Init(int2 max, int res, int smoothRad) } void SmoothHeightMesh::Kill() { - // if (maximaHeightMap != nullptr) { - // _mm_free(maximaHeightMap); - // maximaHeightMap = nullptr; - // } maximaMesh.clear(); mesh.clear(); origMesh.clear(); @@ -111,20 +109,21 @@ inline static void FindMaximumColumnHeights( // height per column and the corresponding row for (int y = min.y; y <= std::min(max.y, min.y + winSize); ++y) { for (int x = min.x; x <= max.x; ++x) { - //const float curx = x * resolution; - //const float cury = y * resolution; - //const float curh = CGround::GetHeightReal(curx, cury); const float curh = readMap->GetCornerHeightMapSynced()[(x + y*mapDims.mapxp1)*resolution]; - if (curh > colsMaxima[x]) { + // LOG("%s: y:%d col:%d cur: %f", __func__, y, x, curh); + + if (curh >= colsMaxima[x]) { colsMaxima[x] = curh; maximaRows[x] = y; } } } + // for (int x = min.x; x <= max.x; ++x) + // LOG("%s: y:%d col:%d row:%d max: %f", __func__, min.y + winSize, x, maximaRows[x], colsMaxima[x]); } -inline static void AdvanceMaximaRows( +inline static void AdvanceMaximaRows( // really useful? const int y, const int minx, const int maxx, @@ -132,13 +131,9 @@ inline static void AdvanceMaximaRows( const std::vector& colsMaxima, std::vector& maximaRows ) { - //const float cury = y * resolution; - // try to advance rows if they're equal to current maximum but are further away for (int x = minx; x <= maxx; ++x) { if (maximaRows[x] == (y - 1)) { - //const float curx = x * resolution; - //const float curh = CGround::GetHeightReal(curx, cury); const float curh = readMap->GetCornerHeightMapSynced()[(x + y*mapDims.mapxp1)*resolution]; if (curh == colsMaxima[x]) { @@ -148,6 +143,9 @@ inline static void AdvanceMaximaRows( assert(curh <= colsMaxima[x]); } } + + // for (int x = minx; x <= maxx; ++x) + // LOG("%s: y:%d col:%d row:%d max: %f", __func__, y, x, maximaRows[x], colsMaxima[x]); } @@ -162,22 +160,39 @@ inline static void FindRadialMaximum( const std::vector& colsMaxima, std::vector& mesh ) { - //const float cury = y * resolution; - for (int x = minx; x <= maxx; ++x) { float maxRowHeight = -std::numeric_limits::max(); // find current maximum within radius smoothRadius // (in every column stack) along the current row const int startx = std::max(x - winSize, 0); - const int endx = std::min(maxx, x + winSize); + const int endx = std::min(mapx, x + winSize); - for (int i = startx; i <= endx; ++i) { - // assert(i >= 0); - // assert(i <= maxx); - // assert(CGround::GetHeightReal(i * resolution, cury) <= colsMaxima[i]); + // for (int i = startx; i <= endx; ++i) { // SSE candidate? + // maxRowHeight = std::max(colsMaxima[i], maxRowHeight); + // } - maxRowHeight = std::max(colsMaxima[i], maxRowHeight); + const int endIdx = endx - 3; + __m128 best = _mm_loadu_ps(&colsMaxima[startx]); + for (int i = startx + 4; i < endIdx; i += 4) { + __m128 next = _mm_loadu_ps(&colsMaxima[i]); + best = _mm_max_ps(best, next); + } + + { + __m128 next = _mm_loadu_ps(&colsMaxima[endIdx]); + best = _mm_max_ps(best, next); + } + + { + // split the four values into sets of two and compare + __m128 bestAlt = _mm_movehl_ps(best, best); + best = _mm_max_ps(best, bestAlt); + + // split the two values and compare + bestAlt = _mm_shuffle_ps(best, best, _MM_SHUFFLE(0, 0, 0, 1)); + best = _mm_max_ss(best, bestAlt); + _mm_store_ss(&maxRowHeight, best); } // #ifndef NDEBUG @@ -200,12 +215,18 @@ inline static void FindRadialMaximum( // #endif mesh[x + y * mapx] = maxRowHeight; + + // LOG("%s: y:%d x:%d local max: %f", __func__, y, x, maxRowHeight); + + // for (int x = minx; x <= maxx; ++x) + // LOG("%s: y:%d x:%d local max: %f", __func__, y, x, maxRowHeight); } } inline static void FixRemainingMaxima( + const int mapy, const int minx, const int miny, const int2 max, @@ -215,7 +236,7 @@ inline static void FixRemainingMaxima( std::vector& maximaRows ) { // fix remaining maximums after a pass - const int nextrow = miny + winSize + 1; + const int nextrow = std::min(mapy, miny + winSize + 1); const int nextrowy = nextrow; // * resolution; for (int x = minx; x <= max.x; ++x) { @@ -230,7 +251,7 @@ inline static void FixRemainingMaxima( // find a new maximum if the old one left the window colsMaxima[x] = -std::numeric_limits::max(); - for (int y1 = std::max(0, miny - winSize + 1); y1 <= std::min(max.y, nextrow); ++y1) { + for (int y1 = std::max(0, (miny - winSize) + 1); y1 <= std::min(mapy, nextrow); ++y1) { //const float h = CGround::GetHeightReal(curx, y1 * resolution); const float h = readMap->GetCornerHeightMapSynced()[(x + y1*mapDims.mapxp1)*resolution]; @@ -278,7 +299,8 @@ inline static void BlurHorizontal( ) { const int lineSize = mapSize.x; - for_mt(min.y, max.y+1, [&](const int y) + //for_mt(min.y, max.y+1, [&](const int y) + for (int y = min.y; y < max.y+1; y++) { float avg = 0.0f; float lv = 0; @@ -312,7 +334,7 @@ inline static void BlurHorizontal( rv = mesh[std::max(0, std::min(mapSize.x-1, ri)) + y * lineSize]; li++; ri++; } - }); + }//); } inline static void BlurVertical( @@ -328,7 +350,8 @@ inline static void BlurVertical( //SCOPED_TIMER("Sim::SmoothHeightMesh::BlurVertical"); const int lineSize = mapSize.x; - for_mt(min.x, max.x+1, [&](const int x) + //for_mt(min.x, max.x+1, [&](const int x) + for (int x = min.x; x < max.x+1; x++) { float avg = 0.0f; float lv = 0; @@ -362,7 +385,7 @@ inline static void BlurVertical( rv = mesh[ x + std::max(0, std::min(mapSize.y-1, ri)) * lineSize]; li++; ri++; } - }); + }//); } @@ -392,76 +415,6 @@ inline static void CheckInvariants( } } -// static int mapWidth = 0.f; - -// static float GetLocalMaxima(int2 point, int2 windowSize) { -// float localMax = -std::numeric_limits::max(); - -// auto heightMap = readMap->GetCornerHeightMapSynced(); - -// int yHalfWindow = windowSize.y / 2; -// int yStart = std::max(0, point.y - yHalfWindow); -// int yEnd = std::min(mapDims.mapy, yStart + yHalfWindow); - -// int xHalfWindow = windowSize.x / 2; -// int xStart = std::max(0, point.x - xHalfWindow); -// int xEnd = std::min(mapDims.mapx, xStart + xHalfWindow); - -// for (int y = yStart; y < yEnd; y++) { -// for (int x = xStart; x < xEnd; x++) { -// int pointIndex = x + y * mapWidth; -// localMax = std::max(localMax, heightMap[pointIndex]); -// } -// } - -// return localMax; -// } - -// static void BuildLocalMaximaGrid(std::pmr::vector &maximaHeightMap, int2 point, int2 sampleSize) { -// int windowSize = 20; - -// int yStart = std::max(0, point.y); -// int yEnd = std::min(mapDims.mapy, yStart + sampleSize.y); - -// int xStart = std::max(0, point.x); -// int xEnd = std::min(mapDims.mapx, xStart + sampleSize.x); - -// for (int y = yStart; y < yEnd; y++) { -// for (int x = xStart; x < xEnd; x++) { -// int pointIndex = x + y * mapWidth; -// maximaHeightMap[pointIndex] = GetLocalMaxima(int2(x, y), int2(windowSize, windowSize)); -// } -// } -// } - -// void SmoothHeightMesh::UpdateMapMaximaGrid() { -// constexpr int itemsPerChunk = CacheAlignedMemoryResource::cacheLineLength / sizeof(float); -// size_t chunks = chunksPerLine * mapDims.mapy; - -// for_mt(0, chunks, [&, itemsPerChunk](const int i) { -// int y = i / chunksPerLine; -// int x = i % chunksPerLine; - -// BuildLocalMaximaGrid(maximaHeightMap, int2(x, y), int2(itemsPerChunk, 1)); -// }); -// } - - -// // get built after initial changes applied -// void SmoothHeightMesh::BuildNewMapMaximaGrid() { -// constexpr int itemsPerChunk = CacheAlignedMemoryResource::cacheLineLength / sizeof(float); -// chunksPerLine = (mapDims.mapx / itemsPerChunk) + (mapDims.mapx % itemsPerChunk ? 1: 0); -// cacheAlignMapWidth = chunksPerLine * itemsPerChunk; - -// size_t mapSize = mapDims.mapy*cacheAlignMapWidth; -// maximaHeightMap.resize(mapSize); - -// //maximaHeightMap = (float*)_mm_malloc(memorySize, cacheLineLength); -// //std::pmr::monotonic_buffer_resource mbr(addr, memorySize, std::pmr::null_memory_resource()); -// //maximaHeightMap = new std::pmr::vector(mapSize, &mbr); -// //std::pmr::vector maximaHeightMa(mapSize, &mbr); -// } - void SmoothHeightMesh::UpdateSmoothMesh() { if (gs->frameNum % (GAME_SPEED/2) != 0) return; @@ -470,21 +423,20 @@ void SmoothHeightMesh::UpdateSmoothMesh() const int winSize = smoothRadius / resolution; const int blurSize = std::max(1, winSize / 2); - constexpr int blurPassesCount = 1; int2 impactRadius{winSize, winSize}; // area of map damaged. - int2 damageMin{40,40}; - int2 damageMax{44,44}; + int2 damageMin{64,64}; + int2 damageMax{95,95}; // area of the map which may need a max height change - int2 updateLocation = damageMin - impactRadius; - int2 updateLimit = damageMax + impactRadius; + int2 updateLocation = damageMin;// - impactRadius; + int2 updateLimit = damageMax;// + impactRadius; // area of map to load maximums for to update the updateLocation int2 min = updateLocation - impactRadius; - int2 max = updateLocation + impactRadius; + int2 max = updateLimit + impactRadius; int2 map{maxx, maxy}; updateLocation.x = std::clamp(updateLocation.x, 0, map.x); @@ -496,24 +448,25 @@ void SmoothHeightMesh::UpdateSmoothMesh() max.x = std::clamp(max.x, 0, map.x); max.y = std::clamp(max.y, 0, map.y); - FindMaximumColumnHeights(min, max, winSize, resolution, colsMaxima, maximaRows); + FindMaximumColumnHeights(min, max, winSize, resolution, colsMaxima, maximaRows); for (int y = updateLocation.y; y <= updateLimit.y; ++y) { - AdvanceMaximaRows(y, min.x, max.x, resolution, colsMaxima, maximaRows); FindRadialMaximum(map.x, y, updateLocation.x, updateLimit.x, winSize, resolution, colsMaxima, maximaMesh); - FixRemainingMaxima(min.x, y, max, winSize, resolution, colsMaxima, maximaRows); + FixRemainingMaxima(map.y, min.x, y, max, winSize, resolution, colsMaxima, maximaRows); // #ifdef _DEBUG // CheckInvariants(y, updateLimit.x, updateLimit.y, winSize, resolution, colsMaxima, maximaRows); // #endif } + //std::copy(maximaMesh.begin(), maximaMesh.end(), mesh.begin()); + // actually smooth with approximate Gaussian blur passes - for (int numBlurs = blurPassesCount; numBlurs > 0; --numBlurs) { - BlurHorizontal(map, updateLocation, updateLimit, blurSize, resolution, gaussianKernel, (numBlurs == blurPassesCount) ? maximaMesh : mesh, tempMesh); mesh.swap(tempMesh); - BlurVertical(map, updateLocation, updateLimit, blurSize, resolution, gaussianKernel, mesh, tempMesh); mesh.swap(tempMesh); - } + //for (int numBlurs = blurPassesCount; numBlurs > 0; --numBlurs) { + BlurHorizontal(map, updateLocation, updateLimit, blurSize, resolution, gaussianKernel, maximaMesh, tempMesh); + BlurVertical(map, updateLocation, updateLimit, blurSize, resolution, gaussianKernel, tempMesh, mesh); + // } void SmoothHeightMesh::MakeSmoothMesh() @@ -537,7 +490,6 @@ void SmoothHeightMesh::MakeSmoothMesh() // use sliding window of maximums to reduce computational complexity const int winSize = smoothRadius / resolution; const int blurSize = std::max(1, winSize / 2); - constexpr int blurPassesCount = 1; // const auto fillGaussianKernelFunc = [blurSize](std::vector& gaussianKernel, const float sigma) { // gaussianKernel.resize(blurSize + 1); @@ -566,7 +518,6 @@ void SmoothHeightMesh::MakeSmoothMesh() mesh.resize((maxx) * (maxy), 0.0f); tempMesh.resize((maxx) * (maxy), 0.0f); origMesh.resize((maxx) * (maxy), 0.0f); - colsMaxima.clear(); colsMaxima.resize(maxx, -std::numeric_limits::max()); maximaRows.clear(); @@ -576,23 +527,23 @@ void SmoothHeightMesh::MakeSmoothMesh() int2 max{maxx-1, maxy-1}; int2 map{maxx, maxy}; -// FindMaximumColumnHeights(int2{0, 0}, max, winSize, resolution, colsMaxima, maximaRows); + FindMaximumColumnHeights(int2{0, 0}, max, winSize, resolution, colsMaxima, maximaRows); -// for (int y = 0; y <= max.y; ++y) { -// AdvanceMaximaRows(y, 0, max.x, resolution, colsMaxima, maximaRows); -// FindRadialMaximum(map.x, y, 0, max.x, winSize, resolution, colsMaxima, maximaMesh); -// FixRemainingMaxima(0, y, max, winSize, resolution, colsMaxima, maximaRows); + for (int y = 0; y <= max.y; ++y) { + AdvanceMaximaRows(y, 0, max.x, resolution, colsMaxima, maximaRows); + FindRadialMaximum(map.x, y, 0, max.x, winSize, resolution, colsMaxima, maximaMesh); + FixRemainingMaxima(map.y, 0, y, max, winSize, resolution, colsMaxima, maximaRows); -// // #ifdef _DEBUG -// // CheckInvariants(y, maxx, maxy, winSize, resolution, colsMaxima, maximaRows); -// // #endif -// } +// #ifdef _DEBUG +// CheckInvariants(y, maxx, maxy, winSize, resolution, colsMaxima, maximaRows); +// #endif + } // actually smooth with approximate Gaussian blur passes - for (int numBlurs = blurPassesCount; numBlurs > 0; --numBlurs) { - BlurHorizontal(map, min, max, blurSize, resolution, gaussianKernel, (numBlurs == blurPassesCount) ? maximaMesh : mesh, tempMesh); mesh.swap(tempMesh); - BlurVertical(map, min, max, blurSize, resolution, gaussianKernel, mesh, tempMesh); mesh.swap(tempMesh); - } + //for (int numBlurs = blurPassesCount; numBlurs > 0; --numBlurs) { + BlurHorizontal(map, min, max, blurSize, resolution, gaussianKernel, maximaMesh, tempMesh);// mesh.swap(tempMesh); + BlurVertical(map, min, max, blurSize, resolution, gaussianKernel, tempMesh, mesh);// mesh.swap(tempMesh); + //} // int i = 0; // for (int y =0; y < maxy; y++) diff --git a/rts/Sim/Misc/SmoothHeightMesh.h b/rts/Sim/Misc/SmoothHeightMesh.h index caa621bca3..498e48b17d 100644 --- a/rts/Sim/Misc/SmoothHeightMesh.h +++ b/rts/Sim/Misc/SmoothHeightMesh.h @@ -10,47 +10,12 @@ class CGround; -// class CacheAlignedMemoryResource : public std::pmr::memory_resource -// { -// public: -// constexpr static int cacheLineLength = 64; - -// CacheAlignedMemoryResource() -// : std::pmr::memory_resource() -// {} - -// virtual void* -// do_allocate(size_t __bytes, size_t __alignment) { -// return _mm_malloc(__bytes, cacheLineLength); -// } - -// virtual void -// do_deallocate(void* __p, size_t __bytes, size_t __alignment) { -// _mm_free(__p); -// } - -// virtual bool -// do_is_equal(const memory_resource& __other) const noexcept { -// return (this == &__other); -// }; -// }; - -// inline std::pmr::memory_resource* -// NewCacheAlignedMemoryResource() noexcept -// { -// return new CacheAlignedMemoryResource(); -// } - /** * Provides a GetHeight(x, y) of its own that smooths the mesh. */ class SmoothHeightMesh { public: - // SmoothHeightMesh() - // : maximaHeightMap(NewCacheAlignedMemoryResource()) - // {} - void Init(int2 max, int res, int smoothRad); void Kill(); @@ -93,14 +58,6 @@ class SmoothHeightMesh void UpdateMapMaximaGrid(); void BuildNewMapMaximaGrid(); - - //std::pmr::monotonic_buffer_resource maximaBuffer; - //std::pmr::polymorphic_allocator maximaAllocator; - //std::pmr::vector maximaHeightMap; - //float *maximaHeightMap = nullptr; - - //size_t cacheAlignMapWidth; - //size_t chunksPerLine; }; extern SmoothHeightMesh smoothGround; From 668a6cb4b35cb1be17760193081af90b6bc1093b Mon Sep 17 00:00:00 2001 From: Marcus Hutchings Date: Sat, 28 May 2022 15:58:24 +0100 Subject: [PATCH 07/20] address edge cases. --- rts/Sim/Misc/SmoothHeightMesh.cpp | 87 +++++++++---------------------- rts/Sim/Misc/SmoothHeightMesh.h | 1 - 2 files changed, 25 insertions(+), 63 deletions(-) diff --git a/rts/Sim/Misc/SmoothHeightMesh.cpp b/rts/Sim/Misc/SmoothHeightMesh.cpp index a5a12ff6b7..ca2977f328 100644 --- a/rts/Sim/Misc/SmoothHeightMesh.cpp +++ b/rts/Sim/Misc/SmoothHeightMesh.cpp @@ -20,15 +20,15 @@ SmoothHeightMesh smoothGround; static float Interpolate(float x, float y, const int maxx, const int maxy, const float res, const float* heightmap) { - x = Clamp(x / res, 0.0f, (float)maxx); - y = Clamp(y / res, 0.0f, (float)maxy); + x = Clamp(x / res, 0.0f, ((float)maxx - 1)); + y = Clamp(y / res, 0.0f, ((float)maxy - 1)); const int sx = x; const int sy = y; const float dx = (x - sx); const float dy = (y - sy); - const int sxp1 = std::min(sx + 1, maxx); - const int syp1 = std::min(sy + 1, maxy); + const int sxp1 = std::min(sx + 1, maxx - 1); + const int syp1 = std::min(sy + 1, maxy - 1); const float& h1 = heightmap[sx + sy * maxx]; const float& h2 = heightmap[sxp1 + sy * maxx]; @@ -111,7 +111,7 @@ inline static void FindMaximumColumnHeights( for (int x = min.x; x <= max.x; ++x) { const float curh = readMap->GetCornerHeightMapSynced()[(x + y*mapDims.mapxp1)*resolution]; - // LOG("%s: y:%d col:%d cur: %f", __func__, y, x, curh); + // LOG("%s: y:%d x:%d cur: %f", __func__, y, x, curh); if (curh >= colsMaxima[x]) { colsMaxima[x] = curh; @@ -123,31 +123,6 @@ inline static void FindMaximumColumnHeights( // LOG("%s: y:%d col:%d row:%d max: %f", __func__, min.y + winSize, x, maximaRows[x], colsMaxima[x]); } -inline static void AdvanceMaximaRows( // really useful? - const int y, - const int minx, - const int maxx, - const int resolution, - const std::vector& colsMaxima, - std::vector& maximaRows -) { - // try to advance rows if they're equal to current maximum but are further away - for (int x = minx; x <= maxx; ++x) { - if (maximaRows[x] == (y - 1)) { - const float curh = readMap->GetCornerHeightMapSynced()[(x + y*mapDims.mapxp1)*resolution]; - - if (curh == colsMaxima[x]) { - maximaRows[x] = y; - } - - assert(curh <= colsMaxima[x]); - } - } - - // for (int x = minx; x <= maxx; ++x) - // LOG("%s: y:%d col:%d row:%d max: %f", __func__, y, x, maximaRows[x], colsMaxima[x]); -} - inline static void FindRadialMaximum( @@ -166,7 +141,7 @@ inline static void FindRadialMaximum( // find current maximum within radius smoothRadius // (in every column stack) along the current row const int startx = std::max(x - winSize, 0); - const int endx = std::min(mapx, x + winSize); + const int endx = std::min(maxx, x + winSize); // for (int i = startx; i <= endx; ++i) { // SSE candidate? // maxRowHeight = std::max(colsMaxima[i], maxRowHeight); @@ -226,7 +201,6 @@ inline static void FindRadialMaximum( inline static void FixRemainingMaxima( - const int mapy, const int minx, const int miny, const int2 max, @@ -236,7 +210,7 @@ inline static void FixRemainingMaxima( std::vector& maximaRows ) { // fix remaining maximums after a pass - const int nextrow = std::min(mapy, miny + winSize + 1); + const int nextrow = std::min(max.y, miny + winSize + 1); const int nextrowy = nextrow; // * resolution; for (int x = minx; x <= max.x; ++x) { @@ -251,8 +225,7 @@ inline static void FixRemainingMaxima( // find a new maximum if the old one left the window colsMaxima[x] = -std::numeric_limits::max(); - for (int y1 = std::max(0, (miny - winSize) + 1); y1 <= std::min(mapy, nextrow); ++y1) { - //const float h = CGround::GetHeightReal(curx, y1 * resolution); + for (int y1 = std::max(0, (miny - winSize) + 1); y1 <= std::min(max.y, nextrow); ++y1) { const float h = readMap->GetCornerHeightMapSynced()[(x + y1*mapDims.mapxp1)*resolution]; if (h > colsMaxima[x]) { @@ -265,15 +238,16 @@ inline static void FixRemainingMaxima( } } else if (nextrow <= max.y) { // else, just check if a new maximum has entered the window - //const float h = CGround::GetHeightReal(curx, nextrowy); const float h = readMap->GetCornerHeightMapSynced()[(x + nextrowy*mapDims.mapxp1)*resolution]; - if (h > colsMaxima[x]) { + if (h >= colsMaxima[x]) { colsMaxima[x] = h; maximaRows[x] = nextrow; } } + // LOG("%s: y:%d x:%d column max: %f", __func__, nextrow, x, colsMaxima[x]); + // assert(maximaRows[x] <= nextrow); // assert(maximaRows[x] >= min.y - winSize + 1); @@ -293,7 +267,6 @@ inline static void BlurHorizontal( const int2 max, const int blurSize, const int resolution, - const std::vector& kernel, const std::vector& mesh, std::vector& smoothed ) { @@ -343,7 +316,6 @@ inline static void BlurVertical( const int2 max, const int blurSize, const int resolution, - const std::vector& kernel, const std::vector& mesh, std::vector& smoothed ) { @@ -431,8 +403,8 @@ void SmoothHeightMesh::UpdateSmoothMesh() int2 damageMax{95,95}; // area of the map which may need a max height change - int2 updateLocation = damageMin;// - impactRadius; - int2 updateLimit = damageMax;// + impactRadius; + int2 updateLocation = damageMin; + int2 updateLimit = damageMax; // area of map to load maximums for to update the updateLocation int2 min = updateLocation - impactRadius; @@ -443,30 +415,23 @@ void SmoothHeightMesh::UpdateSmoothMesh() updateLocation.y = std::clamp(updateLocation.y, 0, map.y); updateLimit.x = std::clamp(updateLimit.x, 0, map.x); updateLimit.y = std::clamp(updateLimit.y, 0, map.y); - min.x = std::clamp(min.x, 0, map.x); - min.y = std::clamp(min.y, 0, map.y); - max.x = std::clamp(max.x, 0, map.x); - max.y = std::clamp(max.y, 0, map.y); + min.x = std::clamp(min.x, 0, map.x - 1); + min.y = std::clamp(min.y, 0, map.y - 1); + max.x = std::clamp(max.x, 0, map.x - 1); + max.y = std::clamp(max.y, 0, map.y - 1); FindMaximumColumnHeights(min, max, winSize, resolution, colsMaxima, maximaRows); for (int y = updateLocation.y; y <= updateLimit.y; ++y) { - AdvanceMaximaRows(y, min.x, max.x, resolution, colsMaxima, maximaRows); + //AdvanceMaximaRows(y, min.x, max.x, resolution, colsMaxima, maximaRows); FindRadialMaximum(map.x, y, updateLocation.x, updateLimit.x, winSize, resolution, colsMaxima, maximaMesh); - FixRemainingMaxima(map.y, min.x, y, max, winSize, resolution, colsMaxima, maximaRows); - -// #ifdef _DEBUG -// CheckInvariants(y, updateLimit.x, updateLimit.y, winSize, resolution, colsMaxima, maximaRows); -// #endif + FixRemainingMaxima(min.x, y, max, winSize, resolution, colsMaxima, maximaRows); } //std::copy(maximaMesh.begin(), maximaMesh.end(), mesh.begin()); - // actually smooth with approximate Gaussian blur passes - //for (int numBlurs = blurPassesCount; numBlurs > 0; --numBlurs) { - BlurHorizontal(map, updateLocation, updateLimit, blurSize, resolution, gaussianKernel, maximaMesh, tempMesh); - BlurVertical(map, updateLocation, updateLimit, blurSize, resolution, gaussianKernel, tempMesh, mesh); - // + BlurHorizontal(map, updateLocation, updateLimit, blurSize, resolution, maximaMesh, tempMesh); + BlurVertical(map, updateLocation, updateLimit, blurSize, resolution, tempMesh, mesh); } void SmoothHeightMesh::MakeSmoothMesh() @@ -530,9 +495,9 @@ void SmoothHeightMesh::MakeSmoothMesh() FindMaximumColumnHeights(int2{0, 0}, max, winSize, resolution, colsMaxima, maximaRows); for (int y = 0; y <= max.y; ++y) { - AdvanceMaximaRows(y, 0, max.x, resolution, colsMaxima, maximaRows); + //AdvanceMaximaRows(y, 0, max.x, resolution, colsMaxima, maximaRows); FindRadialMaximum(map.x, y, 0, max.x, winSize, resolution, colsMaxima, maximaMesh); - FixRemainingMaxima(map.y, 0, y, max, winSize, resolution, colsMaxima, maximaRows); + FixRemainingMaxima(0, y, max, winSize, resolution, colsMaxima, maximaRows); // #ifdef _DEBUG // CheckInvariants(y, maxx, maxy, winSize, resolution, colsMaxima, maximaRows); @@ -540,10 +505,8 @@ void SmoothHeightMesh::MakeSmoothMesh() } // actually smooth with approximate Gaussian blur passes - //for (int numBlurs = blurPassesCount; numBlurs > 0; --numBlurs) { - BlurHorizontal(map, min, max, blurSize, resolution, gaussianKernel, maximaMesh, tempMesh);// mesh.swap(tempMesh); - BlurVertical(map, min, max, blurSize, resolution, gaussianKernel, tempMesh, mesh);// mesh.swap(tempMesh); - //} + BlurHorizontal(map, min, max, blurSize, resolution, maximaMesh, tempMesh); + BlurVertical(map, min, max, blurSize, resolution, tempMesh, mesh); // int i = 0; // for (int y =0; y < maxy; y++) diff --git a/rts/Sim/Misc/SmoothHeightMesh.h b/rts/Sim/Misc/SmoothHeightMesh.h index 498e48b17d..4fbfa97e49 100644 --- a/rts/Sim/Misc/SmoothHeightMesh.h +++ b/rts/Sim/Misc/SmoothHeightMesh.h @@ -52,7 +52,6 @@ class SmoothHeightMesh std::vector tempMesh; std::vector origMesh; - std::vector gaussianKernel; std::vector colsMaxima; std::vector maximaRows; From 6e21f9d9a639f75b62ef4ff7a3dcfef0f73db895 Mon Sep 17 00:00:00 2001 From: Marcus Hutchings Date: Mon, 30 May 2022 19:20:52 +0100 Subject: [PATCH 08/20] addressed outstanding issues. --- rts/Map/BasicMapDamage.cpp | 2 + rts/Sim/Misc/SmoothHeightMesh.cpp | 325 +++++++++++++++++++++++------- rts/Sim/Misc/SmoothHeightMesh.h | 16 ++ 3 files changed, 269 insertions(+), 74 deletions(-) diff --git a/rts/Map/BasicMapDamage.cpp b/rts/Map/BasicMapDamage.cpp index ef0d129df5..c788efc17a 100644 --- a/rts/Map/BasicMapDamage.cpp +++ b/rts/Map/BasicMapDamage.cpp @@ -8,6 +8,7 @@ #include "Sim/Misc/GroundBlockingObjectMap.h" #include "Sim/Misc/LosHandler.h" #include "Sim/Misc/QuadField.h" +#include "Sim/Misc/SmoothHeightMesh.h" #include "Sim/Units/Unit.h" #include "Sim/Units/UnitHandler.h" #include "Sim/Path/IPathManager.h" @@ -227,6 +228,7 @@ void CBasicMapDamage::RecalcArea(int x1, int x2, int y1, int y2) readMap->UpdateHeightMapSynced(updRect); featureHandler.TerrainChanged(x1, y1, x2, y2); + smoothGround.OnMapDamage(x1, y1, x2, y2); { SCOPED_TIMER("Sim::BasicMapDamage::Los"); losHandler->UpdateHeightMapSynced(updRect); diff --git a/rts/Sim/Misc/SmoothHeightMesh.cpp b/rts/Sim/Misc/SmoothHeightMesh.cpp index ca2977f328..653bb0551c 100644 --- a/rts/Sim/Misc/SmoothHeightMesh.cpp +++ b/rts/Sim/Misc/SmoothHeightMesh.cpp @@ -15,6 +15,9 @@ #include "System/Log/ILog.h" +constexpr int SMOOTH_MESH_UPDATE_DELAY = GAME_SPEED; +constexpr int samplesPerQuad = 32; + SmoothHeightMesh smoothGround; @@ -59,6 +62,11 @@ void SmoothHeightMesh::Init(int2 max, int res, int smoothRad) } void SmoothHeightMesh::Kill() { + while (!meshDamageTrack.damageQueue[0].empty()) { meshDamageTrack.damageQueue[0].pop(); } + while (!meshDamageTrack.damageQueue[1].empty()) { meshDamageTrack.damageQueue[1].pop(); } + while (!meshDamageTrack.horizontalBlurQueue.empty()) { meshDamageTrack.horizontalBlurQueue.pop(); } + + meshDamageTrack.damageMap.clear(); maximaMesh.clear(); mesh.clear(); origMesh.clear(); @@ -95,11 +103,27 @@ float SmoothHeightMesh::SetMaxHeight(int index, float h) return (mesh[index] = std::max(h, mesh[index])); } +inline static float GetGroundHeight(int x, int y, int resolution) { + const float* heightMap = readMap->GetCornerHeightMapSynced(); + const int baseIndex = (x + y*mapDims.mapxp1)*resolution; + return heightMap[baseIndex]; + + // float max = -std::numeric_limits::max(); + // for (int y1 = y*resolution; y1 < (y+1)*resolution; ++y1) { + // for (int x1 = x*resolution; x1 < (x+1)*resolution; ++x1) { + // float h = heightMap[x1 + y1*mapDims.mapxp1]; + // if (h > max) max = h; + // } + // } + // return max; +} inline static void FindMaximumColumnHeights( - const int2 min, - const int2 max, + const int2 map, + const int y, + const int minx, + const int maxx, const int winSize, const int resolution, std::vector& colsMaxima, @@ -107,26 +131,30 @@ inline static void FindMaximumColumnHeights( ) { // initialize the algorithm: find the maximum // height per column and the corresponding row - for (int y = min.y; y <= std::min(max.y, min.y + winSize); ++y) { - for (int x = min.x; x <= max.x; ++x) { - const float curh = readMap->GetCornerHeightMapSynced()[(x + y*mapDims.mapxp1)*resolution]; + + const int miny = std::max(y - winSize, 0); + const int maxy = std::min(y + winSize, map.y - 1); + + for (int y1 = miny; y1 <= maxy; ++y1) { + for (int x = minx; x <= maxx; ++x) { + // const float curh = readMap->GetCornerHeightMapSynced()[(x + y*mapDims.mapxp1)*resolution]; + const float curh = GetGroundHeight(x, y1, resolution); // LOG("%s: y:%d x:%d cur: %f", __func__, y, x, curh); if (curh >= colsMaxima[x]) { colsMaxima[x] = curh; - maximaRows[x] = y; + maximaRows[x] = y1; } } } - // for (int x = min.x; x <= max.x; ++x) - // LOG("%s: y:%d col:%d row:%d max: %f", __func__, min.y + winSize, x, maximaRows[x], colsMaxima[x]); + // for (int x = minx; x <= maxx; ++x) + // LOG("%s: y:%d col:%d row:%d max: %f", __func__, y, x, maximaRows[x], colsMaxima[x]); } - inline static void FindRadialMaximum( - int mapx, + const int2 map, int y, int minx, int maxx, @@ -141,7 +169,7 @@ inline static void FindRadialMaximum( // find current maximum within radius smoothRadius // (in every column stack) along the current row const int startx = std::max(x - winSize, 0); - const int endx = std::min(maxx, x + winSize); + const int endx = std::min(x + winSize, map.x - 1); // for (int i = startx; i <= endx; ++i) { // SSE candidate? // maxRowHeight = std::max(colsMaxima[i], maxRowHeight); @@ -189,7 +217,7 @@ inline static void FindRadialMaximum( // #endif // #endif - mesh[x + y * mapx] = maxRowHeight; + mesh[x + y * map.x] = maxRowHeight; // LOG("%s: y:%d x:%d local max: %f", __func__, y, x, maxRowHeight); @@ -200,53 +228,52 @@ inline static void FindRadialMaximum( -inline static void FixRemainingMaxima( +inline static void AdvanceMaxima( + const int2 map, + const int y, const int minx, - const int miny, - const int2 max, + const int maxx, const int winSize, const int resolution, std::vector& colsMaxima, std::vector& maximaRows ) { // fix remaining maximums after a pass - const int nextrow = std::min(max.y, miny + winSize + 1); - const int nextrowy = nextrow; // * resolution; + const int miny = std::max(y - winSize, 0); + const int virtualRow = y + winSize; + const int maxy = std::min(virtualRow, map.y - 1); - for (int x = minx; x <= max.x; ++x) { + for (int x = minx; x <= maxx; ++x) { // #ifdef _DEBUG // for (int y1 = std::max(min.y, min.y - winSize); y1 <= std::min(max.y, min.y + winSize); ++y1) { // assert(CGround::GetHeightReal(x * resolution, y1 * resolution) <= colsMaxima[x]); // } // #endif - //const int curx = x * resolution; - - if (maximaRows[x] <= (miny - winSize)) { + if (maximaRows[x] < miny) { // find a new maximum if the old one left the window colsMaxima[x] = -std::numeric_limits::max(); - for (int y1 = std::max(0, (miny - winSize) + 1); y1 <= std::min(max.y, nextrow); ++y1) { - const float h = readMap->GetCornerHeightMapSynced()[(x + y1*mapDims.mapxp1)*resolution]; + for (int y1 = miny; y1 <= maxy; ++y1) { + // const float h = readMap->GetCornerHeightMapSynced()[(x + y1*mapDims.mapxp1)*resolution]; + const float h = GetGroundHeight(x, y1, resolution); - if (h > colsMaxima[x]) { + if (h >= colsMaxima[x]) { colsMaxima[x] = h; maximaRows[x] = y1; - } else if (colsMaxima[x] == h) { - // if equal, move as far down as possible - maximaRows[x] = y1; } } - } else if (nextrow <= max.y) { + } else if (virtualRow < map.y) { // else, just check if a new maximum has entered the window - const float h = readMap->GetCornerHeightMapSynced()[(x + nextrowy*mapDims.mapxp1)*resolution]; + // const float h = readMap->GetCornerHeightMapSynced()[(x + maxy*mapDims.mapxp1)*resolution]; + const float h = GetGroundHeight(x, maxy, resolution); if (h >= colsMaxima[x]) { colsMaxima[x] = h; - maximaRows[x] = nextrow; + maximaRows[x] = maxy; } } - // LOG("%s: y:%d x:%d column max: %f", __func__, nextrow, x, colsMaxima[x]); + // LOG("%s: y:%d x:%d column max: %f", __func__, y, x, colsMaxima[x]); // assert(maximaRows[x] <= nextrow); // assert(maximaRows[x] >= min.y - winSize + 1); @@ -271,28 +298,34 @@ inline static void BlurHorizontal( std::vector& smoothed ) { const int lineSize = mapSize.x; + const int mapMaxX = mapSize.x - 1; //for_mt(min.y, max.y+1, [&](const int y) - for (int y = min.y; y < max.y+1; y++) + for (int y = min.y; y <= max.y; ++y) { float avg = 0.0f; float lv = 0; float rv = 0; - float weight = 1.f / ((float)blurSize * 2.f + 1.f); + float weight = 1.f / ((float)(blurSize*2 + 1)); int li = min.x - blurSize; - int ri = min.x + blurSize + 1; - for (int x1 = li; x1 < ri; ++x1) - avg += mesh[std::max(0, std::min(mapSize.x-1, x1)) + y * lineSize]; + int ri = min.x + blurSize; + for (int x1 = li; x1 <= ri; ++x1) { + avg += mesh[std::max(0, std::min(x1, mapMaxX)) + y * lineSize]; + } + ri++; - for (int x = min.x; x < max.x; ++x) + for (int x = min.x; x <= max.x; ++x) { avg += (-lv) + rv; //const float ghaw = CGround::GetHeightReal(x * resolution, y * resolution); - const float ghaw = readMap->GetCornerHeightMapSynced()[(x + y*mapDims.mapxp1)*resolution]; + // const float ghaw = readMap->GetCornerHeightMapSynced()[(x + y*mapDims.mapxp1)*resolution]; + const float ghaw = GetGroundHeight(x, y, resolution); smoothed[x + y * lineSize] = std::max(ghaw, avg*weight); + // LOG("%s: x: %d, y: %d, avg: %f (%f) (g: %f)", __func__, x, y, avg, avg*weight, ghaw); + // #pragma message ("FIX ME") // smoothed[x + y * lineSize] = std::clamp( // smoothed[x + y * lineSize], @@ -303,8 +336,8 @@ inline static void BlurHorizontal( // assert(smoothed[x + y * lineSize] <= readMap->GetCurrMaxHeight() ); // assert(smoothed[x + y * lineSize] >= readMap->GetCurrMinHeight() ); - lv = mesh[std::max(0, std::min(mapSize.x-1, li)) + y * lineSize]; - rv = mesh[std::max(0, std::min(mapSize.x-1, ri)) + y * lineSize]; + lv = mesh[std::max(0, std::min(li, mapMaxX)) + y * lineSize]; + rv = mesh[ std::min(ri, mapMaxX) + y * lineSize]; li++; ri++; } }//); @@ -321,25 +354,32 @@ inline static void BlurVertical( ) { //SCOPED_TIMER("Sim::SmoothHeightMesh::BlurVertical"); const int lineSize = mapSize.x; + const int mapMaxY = mapSize.y - 1; //for_mt(min.x, max.x+1, [&](const int x) - for (int x = min.x; x < max.x+1; x++) + for (int x = min.x; x <= max.x; ++x) { float avg = 0.0f; float lv = 0; float rv = 0; - float weight = 1.f / ((float)blurSize * 2.f + 1.f); + float weight = 1.f / ((float)(blurSize*2 + 1)); int li = min.y - blurSize; - int ri = min.y + blurSize + 1; - for (int y1 = li; y1 < ri; ++y1) - avg += mesh[ x + std::max(0, std::min(mapSize.y-1, y1)) * lineSize]; + int ri = min.y + blurSize; + for (int y1 = li; y1 <= ri; ++y1) { + avg += mesh[x + std::max(0, std::min(y1, mapMaxY)) * lineSize]; + } + // LOG("%s: starting average is: %f (%f) (w: %f)", __func__, avg, avg*weight, weight); + ri++; // ri points to the next value to add - for (int y = min.y; y < max.y; ++y) + for (int y = min.y; y <= max.y; ++y) { avg += (-lv) + rv; //const float ghaw = CGround::GetHeightReal(x * resolution, y * resolution); - const float ghaw = readMap->GetCornerHeightMapSynced()[(x + y*mapDims.mapxp1)*resolution]; + // const float ghaw = readMap->GetCornerHeightMapSynced()[(x + y*mapDims.mapxp1)*resolution]; + const float ghaw = GetGroundHeight(x, y, resolution); + + // LOG("%s: x: %d, y: %d, avg: %f (%f) (g: %f)", __func__, x, y, avg, avg*weight, ghaw); smoothed[x + y * lineSize] = std::max(ghaw, avg*weight); @@ -353,9 +393,11 @@ inline static void BlurVertical( // assert(smoothed[x + y * lineSize] <= readMap->GetCurrMaxHeight() ); // assert(smoothed[x + y * lineSize] >= readMap->GetCurrMinHeight() ); - lv = mesh[ x + std::max(0, std::min(mapSize.y-1, li)) * lineSize]; - rv = mesh[ x + std::max(0, std::min(mapSize.y-1, ri)) * lineSize]; + lv = mesh[ x + std::max(0, std::min(li, mapMaxY)) * lineSize]; + rv = mesh[ x + std::min(ri, mapMaxY) * lineSize]; li++; ri++; + + // LOG("%s: for next line -%f +%f", __func__, lv, rv); } }//); } @@ -387,51 +429,176 @@ inline static void CheckInvariants( } } +void SmoothHeightMesh::OnMapDamage(int x1, int y1, int x2, int y2) { + + const bool queueWasEmpty = meshDamageTrack.damageQueue[meshDamageTrack.activeBuffer].empty(); + const int res = resolution*samplesPerQuad; + const int w = meshDamageTrack.width; + const int h = meshDamageTrack.height; + + const int2 min { std::max((x1 - smoothRadius) / res, 0) + , std::max((y1 - smoothRadius) / res, 0)}; + const int2 max { std::min((x2 + smoothRadius - 1) / res, (w-1)) + , std::min((y2 + smoothRadius - 1) / res, (h-1))}; + + for (int y = min.y; y <= max.y; ++y) { + int i = min.x + y*w; + for (int x = min.x; x <= max.x; ++x, ++i) { + if (!meshDamageTrack.damageMap[i]) { + meshDamageTrack.damageMap[i] = true; + meshDamageTrack.damageQueue[meshDamageTrack.activeBuffer].push(i); + } + } + } + + const bool queueWasUpdated = !meshDamageTrack.damageQueue[meshDamageTrack.activeBuffer].empty(); + + if (queueWasEmpty && queueWasUpdated) + meshDamageTrack.queueReleaseOnFrame = gs->frameNum + SMOOTH_MESH_UPDATE_DELAY; +} + + void SmoothHeightMesh::UpdateSmoothMesh() { - if (gs->frameNum % (GAME_SPEED/2) != 0) return; - SCOPED_TIMER("Sim::SmoothHeightMesh::UpdateSmoothMesh"); + const bool flushBuffer = !meshDamageTrack.activeBuffer; + const bool activeBuffer = meshDamageTrack.activeBuffer; + const bool currentWorkloadComplete = meshDamageTrack.damageQueue[flushBuffer].empty() + && meshDamageTrack.horizontalBlurQueue.empty() + && meshDamageTrack.verticalBlurQueue.empty(); + + // LOG("%s: flush buffer is %d; damage queue is %I64d; blur queue is %I64d", + // __func__, + // (int)flushBuffer, + // meshDamageTrack.damageQueue[flushBuffer].size(), + // meshDamageTrack.blurQueue.size() + // ); + + if (currentWorkloadComplete){ + const bool activeBufferReady = !meshDamageTrack.damageQueue[activeBuffer].empty() + && gs->frameNum >= meshDamageTrack.queueReleaseOnFrame; + if (activeBufferReady) { + meshDamageTrack.activeBuffer = !meshDamageTrack.activeBuffer; + // LOG("%s: opening new queue", __func__); + } + return; + } + + bool updateMaxima = !meshDamageTrack.damageQueue[flushBuffer].empty(); + bool doHorizontalBlur = !meshDamageTrack.horizontalBlurQueue.empty(); + int damagedAreaIndex = 0; + + std::queue* activeQueue = nullptr; + + if (updateMaxima) + activeQueue = &meshDamageTrack.damageQueue[flushBuffer]; + else if (doHorizontalBlur) { + activeQueue = &meshDamageTrack.horizontalBlurQueue; + } else { + activeQueue = &meshDamageTrack.verticalBlurQueue; + } + + damagedAreaIndex = activeQueue->front(); + activeQueue->pop(); + const int winSize = smoothRadius / resolution; const int blurSize = std::max(1, winSize / 2); int2 impactRadius{winSize, winSize}; - // area of map damaged. - int2 damageMin{64,64}; - int2 damageMax{95,95}; + const int damageX = damagedAreaIndex % meshDamageTrack.width; + const int damageY = damagedAreaIndex / meshDamageTrack.width; + // area of map damaged. // area of the map which may need a max height change - int2 updateLocation = damageMin; - int2 updateLimit = damageMax; + int2 damageMin{damageX*samplesPerQuad, damageY*samplesPerQuad}; + int2 damageMax = damageMin + int2{samplesPerQuad - 1, samplesPerQuad - 1}; // area of map to load maximums for to update the updateLocation - int2 min = updateLocation - impactRadius; - int2 max = updateLimit + impactRadius; + int2 min = damageMin - impactRadius; + int2 max = damageMax + impactRadius; int2 map{maxx, maxy}; - updateLocation.x = std::clamp(updateLocation.x, 0, map.x); - updateLocation.y = std::clamp(updateLocation.y, 0, map.y); - updateLimit.x = std::clamp(updateLimit.x, 0, map.x); - updateLimit.y = std::clamp(updateLimit.y, 0, map.y); + damageMin.x = std::clamp(damageMin.x, 0, map.x); + damageMin.y = std::clamp(damageMin.y, 0, map.y); + damageMax.x = std::clamp(damageMax.x, 0, map.x); + damageMax.y = std::clamp(damageMax.y, 0, map.y); min.x = std::clamp(min.x, 0, map.x - 1); min.y = std::clamp(min.y, 0, map.y - 1); max.x = std::clamp(max.x, 0, map.x - 1); max.y = std::clamp(max.y, 0, map.y - 1); - FindMaximumColumnHeights(min, max, winSize, resolution, colsMaxima, maximaRows); + if (updateMaxima) { - for (int y = updateLocation.y; y <= updateLimit.y; ++y) { - //AdvanceMaximaRows(y, min.x, max.x, resolution, colsMaxima, maximaRows); - FindRadialMaximum(map.x, y, updateLocation.x, updateLimit.x, winSize, resolution, colsMaxima, maximaMesh); - FixRemainingMaxima(min.x, y, max, winSize, resolution, colsMaxima, maximaRows); - } + // LOG("%s: quad index %d (%d,%d)-(%d,%d) (%d,%d)-(%d,%d) updating maxima", + // __func__, + // damagedAreaIndex, + // damageMin.x, damageMin.y, + // damageMax.x, damageMax.y, + // min.x, min.y, + // max.x, max.y + // ); - //std::copy(maximaMesh.begin(), maximaMesh.end(), mesh.begin()); + // LOG("%s: quad area in world space (%f,%f) (%f,%f)", __func__ + // , (float)damageMin.x * fresolution + // , (float)damageMin.y * fresolution + // , (float)damageMax.x * fresolution + // , (float)damageMax.y * fresolution + // ); - BlurHorizontal(map, updateLocation, updateLimit, blurSize, resolution, maximaMesh, tempMesh); - BlurVertical(map, updateLocation, updateLimit, blurSize, resolution, tempMesh, mesh); + for (int i = min.x; i <= max.x; ++i) + colsMaxima[i] = -std::numeric_limits::max(); + + for (int i = min.x; i <= max.x; ++i) + maximaRows[i] = -1; + + FindMaximumColumnHeights(map, damageMin.y, min.x, max.x, winSize, resolution, colsMaxima, maximaRows); + + for (int y = damageMin.y; y <= damageMax.y; ++y) { + FindRadialMaximum(map, y, damageMin.x, damageMax.x, winSize, resolution, colsMaxima, maximaMesh); + AdvanceMaxima(map, y+1, min.x, max.x, winSize, resolution, colsMaxima, maximaRows); + } + //std::copy(maximaMesh.begin(), maximaMesh.end(), mesh.begin()); + + meshDamageTrack.horizontalBlurQueue.push(damagedAreaIndex); + meshDamageTrack.damageMap[damagedAreaIndex] = false; + } else { + + // LOG("%s: quad index %d (%d,%d)-(%d,%d) (%d,%d)-(%d,%d) applying blur", + // __func__, + // damagedAreaIndex, + // damageMin.x, damageMin.y, + // damageMax.x, damageMax.y, + // min.x, min.y, + // max.x, max.y + // ); + + // LOG("%s: quad area in world space (%f,%f) (%f,%f)", __func__ + // , (float)damageMin.x * fresolution + // , (float)damageMin.y * fresolution + // , (float)damageMax.x * fresolution + // , (float)damageMax.y * fresolution + // ); + + // BlurHorizontal(map, damageMin, damageMax, blurSize, resolution, maximaMesh, mesh); + // BlurVertical(map, damageMin, damageMax, blurSize, resolution, maximaMesh, mesh); + if (doHorizontalBlur) { + BlurHorizontal(map, damageMin, damageMax, blurSize, resolution, maximaMesh, tempMesh); + meshDamageTrack.verticalBlurQueue.push(damagedAreaIndex); + } + else { + BlurVertical(map, damageMin, damageMax, blurSize, resolution, tempMesh, mesh); + + for (int y = damageMin.y; y <= damageMax.y; ++y) { + const int startIdx = damageMin.x + y*map.x; + const int endIdx = damageMax.x + y*map.x + 1; + std::copy(&mesh[startIdx], &mesh[endIdx], &tempMesh[startIdx]); + } + } + } + + // std::copy(maximaMesh.begin(), maximaMesh.end(), mesh.begin()); } void SmoothHeightMesh::MakeSmoothMesh() @@ -478,6 +645,15 @@ void SmoothHeightMesh::MakeSmoothMesh() // constexpr float gSigma = 5.0f; // fillGaussianKernelFunc(gaussianKernel, gSigma); + + const int damageTrackWidth = maxx / samplesPerQuad + (maxx % samplesPerQuad ? 1 : 0); + const int damageTrackHeight = maxy / samplesPerQuad + (maxy % samplesPerQuad ? 1 : 0); + const int damageTrackQuads = damageTrackWidth * damageTrackHeight; + meshDamageTrack.damageMap.clear(); + meshDamageTrack.damageMap.resize(damageTrackQuads); + meshDamageTrack.width = damageTrackWidth; + meshDamageTrack.height = damageTrackHeight; + assert(mesh.empty()); maximaMesh.resize((maxx) * (maxy), 0.0f); mesh.resize((maxx) * (maxy), 0.0f); @@ -492,12 +668,12 @@ void SmoothHeightMesh::MakeSmoothMesh() int2 max{maxx-1, maxy-1}; int2 map{maxx, maxy}; - FindMaximumColumnHeights(int2{0, 0}, max, winSize, resolution, colsMaxima, maximaRows); + FindMaximumColumnHeights(map, 0, 0, max.x, winSize, resolution, colsMaxima, maximaRows); for (int y = 0; y <= max.y; ++y) { //AdvanceMaximaRows(y, 0, max.x, resolution, colsMaxima, maximaRows); - FindRadialMaximum(map.x, y, 0, max.x, winSize, resolution, colsMaxima, maximaMesh); - FixRemainingMaxima(0, y, max, winSize, resolution, colsMaxima, maximaRows); + FindRadialMaximum(map, y, 0, max.x, winSize, resolution, colsMaxima, maximaMesh); + AdvanceMaxima(map, y+1, 0, max.x, winSize, resolution, colsMaxima, maximaRows); // #ifdef _DEBUG // CheckInvariants(y, maxx, maxy, winSize, resolution, colsMaxima, maximaRows); @@ -516,6 +692,7 @@ void SmoothHeightMesh::MakeSmoothMesh() // now contains the final smoothed heightmap, save it in origMesh std::copy(mesh.begin(), mesh.end(), origMesh.begin()); + std::copy(mesh.begin(), mesh.end(), tempMesh.begin()); // std::copy(maximaMesh.begin(), maximaMesh.end(), origMesh.begin()); // std::copy(maximaMesh.begin(), maximaMesh.end(), mesh.begin()); } diff --git a/rts/Sim/Misc/SmoothHeightMesh.h b/rts/Sim/Misc/SmoothHeightMesh.h index 4fbfa97e49..53e4264fa4 100644 --- a/rts/Sim/Misc/SmoothHeightMesh.h +++ b/rts/Sim/Misc/SmoothHeightMesh.h @@ -4,12 +4,24 @@ #define SMOOTH_HEIGHT_MESH_H #include +#include #include #include "System/type2.h" class CGround; +struct DamageMesh { + std::vector damageMap; + std::queue damageQueue[2]; + std::queue horizontalBlurQueue; + std::queue verticalBlurQueue; + int width = 0; + int height = 0; + int queueReleaseOnFrame = 0; + bool activeBuffer = 0; +}; + /** * Provides a GetHeight(x, y) of its own that smooths the mesh. */ @@ -36,6 +48,8 @@ class SmoothHeightMesh void UpdateSmoothMesh(); + void OnMapDamage(int x1, int z1, int x2, int z2); + private: void MakeSmoothMesh(); @@ -55,6 +69,8 @@ class SmoothHeightMesh std::vector colsMaxima; std::vector maximaRows; + DamageMesh meshDamageTrack; + void UpdateMapMaximaGrid(); void BuildNewMapMaximaGrid(); }; From 683fd88a6568289f160a976b120f7907298bae69 Mon Sep 17 00:00:00 2001 From: Marcus Hutchings Date: Mon, 30 May 2022 21:19:51 +0100 Subject: [PATCH 09/20] Tidying code. --- rts/Map/BasicMapDamage.cpp | 2 +- rts/Sim/Misc/SmoothHeightMesh.cpp | 420 ++++++++++++++---------------- rts/Sim/Misc/SmoothHeightMesh.h | 29 ++- 3 files changed, 208 insertions(+), 243 deletions(-) diff --git a/rts/Map/BasicMapDamage.cpp b/rts/Map/BasicMapDamage.cpp index c788efc17a..349d4f7910 100644 --- a/rts/Map/BasicMapDamage.cpp +++ b/rts/Map/BasicMapDamage.cpp @@ -228,7 +228,7 @@ void CBasicMapDamage::RecalcArea(int x1, int x2, int y1, int y2) readMap->UpdateHeightMapSynced(updRect); featureHandler.TerrainChanged(x1, y1, x2, y2); - smoothGround.OnMapDamage(x1, y1, x2, y2); + smoothGround.MapChanged(x1, y1, x2, y2); { SCOPED_TIMER("Sim::BasicMapDamage::Los"); losHandler->UpdateHeightMapSynced(updRect); diff --git a/rts/Sim/Misc/SmoothHeightMesh.cpp b/rts/Sim/Misc/SmoothHeightMesh.cpp index 653bb0551c..85a9435859 100644 --- a/rts/Sim/Misc/SmoothHeightMesh.cpp +++ b/rts/Sim/Misc/SmoothHeightMesh.cpp @@ -15,8 +15,20 @@ #include "System/Log/ILog.h" +#if 0 +#define SMOOTH_MESH_DEBUG_MAXIMA +#endif + +#if 0 +#define SMOOTH_MESH_DEBUG_BLUR +#endif + +#if 0 +#define SMOOTH_MESH_DEBUG_GENERAL +#endif + constexpr int SMOOTH_MESH_UPDATE_DELAY = GAME_SPEED; -constexpr int samplesPerQuad = 32; +constexpr int SAMPLES_PER_QUAD = 32; SmoothHeightMesh smoothGround; @@ -43,11 +55,13 @@ static float Interpolate(float x, float y, const int maxx, const int maxy, const return mix(hi1, hi2, dy); } - void SmoothHeightMesh::Init(int2 max, int res, int smoothRad) { Kill(); + // we use SSE in performance sensitive code, don't let the window size be too small. + if (smoothRad < 4) smoothRad = 4; + fmaxx = max.x * SQUARE_SIZE; fmaxy = max.y * SQUARE_SIZE; fresolution = res * SQUARE_SIZE; @@ -58,6 +72,8 @@ void SmoothHeightMesh::Init(int2 max, int res, int smoothRad) smoothRadius = std::max(1, smoothRad); + InitMapChangeTracking(); + InitDataStructures(); MakeSmoothMesh(); } @@ -65,6 +81,7 @@ void SmoothHeightMesh::Kill() { while (!meshDamageTrack.damageQueue[0].empty()) { meshDamageTrack.damageQueue[0].pop(); } while (!meshDamageTrack.damageQueue[1].empty()) { meshDamageTrack.damageQueue[1].pop(); } while (!meshDamageTrack.horizontalBlurQueue.empty()) { meshDamageTrack.horizontalBlurQueue.pop(); } + while (!meshDamageTrack.verticalBlurQueue.empty()) { meshDamageTrack.verticalBlurQueue.pop(); } meshDamageTrack.damageMap.clear(); maximaMesh.clear(); @@ -72,8 +89,6 @@ void SmoothHeightMesh::Kill() { origMesh.clear(); } - - float SmoothHeightMesh::GetHeight(float x, float y) { assert(!mesh.empty()); @@ -86,8 +101,6 @@ float SmoothHeightMesh::GetHeightAboveWater(float x, float y) return std::max(0.0f, Interpolate(x, y, maxx, maxy, resolution, &mesh[0])); } - - float SmoothHeightMesh::SetHeight(int index, float h) { return (mesh[index] = h); @@ -103,20 +116,11 @@ float SmoothHeightMesh::SetMaxHeight(int index, float h) return (mesh[index] = std::max(h, mesh[index])); } -inline static float GetGroundHeight(int x, int y, int resolution) { +inline static float GetRealGroundHeight(int x, int y, int resolution) { const float* heightMap = readMap->GetCornerHeightMapSynced(); const int baseIndex = (x + y*mapDims.mapxp1)*resolution; return heightMap[baseIndex]; - - // float max = -std::numeric_limits::max(); - // for (int y1 = y*resolution; y1 < (y+1)*resolution; ++y1) { - // for (int x1 = x*resolution; x1 < (x+1)*resolution; ++x1) { - // float h = heightMap[x1 + y1*mapDims.mapxp1]; - // if (h > max) max = h; - // } - // } - // return max; } inline static void FindMaximumColumnHeights( @@ -137,10 +141,7 @@ inline static void FindMaximumColumnHeights( for (int y1 = miny; y1 <= maxy; ++y1) { for (int x = minx; x <= maxx; ++x) { - // const float curh = readMap->GetCornerHeightMapSynced()[(x + y*mapDims.mapxp1)*resolution]; - const float curh = GetGroundHeight(x, y1, resolution); - - // LOG("%s: y:%d x:%d cur: %f", __func__, y, x, curh); + const float curh = GetRealGroundHeight(x, y1, resolution); if (curh >= colsMaxima[x]) { colsMaxima[x] = curh; @@ -148,8 +149,11 @@ inline static void FindMaximumColumnHeights( } } } - // for (int x = minx; x <= maxx; ++x) - // LOG("%s: y:%d col:%d row:%d max: %f", __func__, y, x, maximaRows[x], colsMaxima[x]); + +#ifdef SMOOTH_MESH_DEBUG_MAXIMA + for (int x = minx; x <= maxx; ++x) + LOG("%s: y:%d col:%d row:%d max: %f", __func__, y, x, maximaRows[x], colsMaxima[x]); +#endif } @@ -171,22 +175,24 @@ inline static void FindRadialMaximum( const int startx = std::max(x - winSize, 0); const int endx = std::min(x + winSize, map.x - 1); - // for (int i = startx; i <= endx; ++i) { // SSE candidate? - // maxRowHeight = std::max(colsMaxima[i], maxRowHeight); - // } - + // This may mean the last SSE max function compares some values have already been compared + // This is harmless and avoids needlessly messy SSE code here const int endIdx = endx - 3; + + // Main loop for finding maximum height values __m128 best = _mm_loadu_ps(&colsMaxima[startx]); for (int i = startx + 4; i < endIdx; i += 4) { __m128 next = _mm_loadu_ps(&colsMaxima[i]); best = _mm_max_ps(best, next); } + // Check the last few height values { __m128 next = _mm_loadu_ps(&colsMaxima[endIdx]); best = _mm_max_ps(best, next); } + // This is an SSE horizontal compare { // split the four values into sets of two and compare __m128 bestAlt = _mm_movehl_ps(best, best); @@ -198,37 +204,21 @@ inline static void FindRadialMaximum( _mm_store_ss(&maxRowHeight, best); } -// #ifndef NDEBUG -// const float curx = x * resolution; -// assert(maxRowHeight <= readMap->GetCurrMaxHeight()); -// assert(maxRowHeight >= CGround::GetHeightReal(curx, cury)); - -// #ifdef SMOOTHMESH_CORRECTNESS_CHECK -// // naive algorithm -// float maxRowHeightAlt = -std::numeric_limits::max(); - -// for (float y1 = cury - smoothRadius; y1 <= cury + smoothRadius; y1 += resolution) { -// for (float x1 = curx - smoothRadius; x1 <= curx + smoothRadius; x1 += resolution) { -// maxRowHeightAlt = std::max(maxRowHeightAlt, CGround::GetHeightReal(x1, y1)); -// } -// } - -// assert(maxRowHeightAlt == maxRowHeight); -// #endif -// #endif +#ifndef NDEBUG + assert(maxRowHeight <= readMap->GetCurrMaxHeight()); + assert(maxRowHeight >= GetRealGroundHeight(x, y, resolution)); +#endif mesh[x + y * map.x] = maxRowHeight; - // LOG("%s: y:%d x:%d local max: %f", __func__, y, x, maxRowHeight); - - // for (int x = minx; x <= maxx; ++x) - // LOG("%s: y:%d x:%d local max: %f", __func__, y, x, maxRowHeight); +#ifdef SMOOTH_MESH_DEBUG_MAXIMA + LOG("%s: y:%d x:%d local max: %f", __func__, y, x, maxRowHeight); +#endif } } - -inline static void AdvanceMaxima( +inline static void AdvanceMaximas( const int2 map, const int y, const int minx, @@ -244,18 +234,12 @@ inline static void AdvanceMaxima( const int maxy = std::min(virtualRow, map.y - 1); for (int x = minx; x <= maxx; ++x) { -// #ifdef _DEBUG -// for (int y1 = std::max(min.y, min.y - winSize); y1 <= std::min(max.y, min.y + winSize); ++y1) { -// assert(CGround::GetHeightReal(x * resolution, y1 * resolution) <= colsMaxima[x]); -// } -// #endif if (maximaRows[x] < miny) { // find a new maximum if the old one left the window colsMaxima[x] = -std::numeric_limits::max(); for (int y1 = miny; y1 <= maxy; ++y1) { - // const float h = readMap->GetCornerHeightMapSynced()[(x + y1*mapDims.mapxp1)*resolution]; - const float h = GetGroundHeight(x, y1, resolution); + const float h = GetRealGroundHeight(x, y1, resolution); if (h >= colsMaxima[x]) { colsMaxima[x] = h; @@ -264,8 +248,7 @@ inline static void AdvanceMaxima( } } else if (virtualRow < map.y) { // else, just check if a new maximum has entered the window - // const float h = readMap->GetCornerHeightMapSynced()[(x + maxy*mapDims.mapxp1)*resolution]; - const float h = GetGroundHeight(x, maxy, resolution); + const float h = GetRealGroundHeight(x, maxy, resolution); if (h >= colsMaxima[x]) { colsMaxima[x] = h; @@ -273,21 +256,18 @@ inline static void AdvanceMaxima( } } - // LOG("%s: y:%d x:%d column max: %f", __func__, y, x, colsMaxima[x]); - - // assert(maximaRows[x] <= nextrow); - // assert(maximaRows[x] >= min.y - winSize + 1); +#ifdef SMOOTH_MESH_DEBUG_MAXIMA + LOG("%s: y:%d x:%d column max: %f", __func__, y, x, colsMaxima[x]); +#endif -// #ifdef _DEBUG -// for (int y1 = std::max(0, min.y - winSize + 1); y1 <= std::min(max.y, min.y + winSize + 1); ++y1) { -// assert(colsMaxima[x] >= CGround::GetHeightReal(curx, y1 * resolution)); -// } -// #endif +#ifndef NDEBUG + assert(maximaRows[x] <= maxy); + assert(maximaRows[x] >= (y - winSize) + 1); +#endif } } - inline static void BlurHorizontal( const int2 mapSize, const int2 min, @@ -300,7 +280,6 @@ inline static void BlurHorizontal( const int lineSize = mapSize.x; const int mapMaxX = mapSize.x - 1; - //for_mt(min.y, max.y+1, [&](const int y) for (int y = min.y; y <= max.y; ++y) { float avg = 0.0f; @@ -309,38 +288,38 @@ inline static void BlurHorizontal( float weight = 1.f / ((float)(blurSize*2 + 1)); int li = min.x - blurSize; int ri = min.x + blurSize; + + // linear blending allows us to add up all the values to average for the first point + // the rest of the points can be determined by taking the average after removing the + // last oldest value and adding the next value. for (int x1 = li; x1 <= ri; ++x1) { avg += mesh[std::max(0, std::min(x1, mapMaxX)) + y * lineSize]; } + // ri should point to the next value to add to the averages ri++; for (int x = min.x; x <= max.x; ++x) { + // Remove the oldest height value (lv) and add the newest height value (rv) avg += (-lv) + rv; + const float gh = GetRealGroundHeight(x, y, resolution); + smoothed[x + y * lineSize] = std::max(gh, avg*weight); - //const float ghaw = CGround::GetHeightReal(x * resolution, y * resolution); - // const float ghaw = readMap->GetCornerHeightMapSynced()[(x + y*mapDims.mapxp1)*resolution]; - const float ghaw = GetGroundHeight(x, y, resolution); - - smoothed[x + y * lineSize] = std::max(ghaw, avg*weight); - - // LOG("%s: x: %d, y: %d, avg: %f (%f) (g: %f)", __func__, x, y, avg, avg*weight, ghaw); - - // #pragma message ("FIX ME") - // smoothed[x + y * lineSize] = std::clamp( - // smoothed[x + y * lineSize], - // readMap->GetCurrMinHeight(), - // readMap->GetCurrMaxHeight() - // ); - - // assert(smoothed[x + y * lineSize] <= readMap->GetCurrMaxHeight() ); - // assert(smoothed[x + y * lineSize] >= readMap->GetCurrMinHeight() ); +#ifndef NDEBUG + assert(smoothed[x + y * lineSize] <= readMap->GetCurrMaxHeight() ); + assert(smoothed[x + y * lineSize] >= readMap->GetCurrMinHeight() ); +#endif + // Get the values to add/remove for next iteration lv = mesh[std::max(0, std::min(li, mapMaxX)) + y * lineSize]; rv = mesh[ std::min(ri, mapMaxX) + y * lineSize]; li++; ri++; + +#ifdef SMOOTH_MESH_DEBUG_BLUR + LOG("%s: x: %d, y: %d, avg: %f (%f) (g: %f)", __func__, x, y, avg, avg*weight, gh); +#endif } - }//); + } } inline static void BlurVertical( @@ -352,11 +331,11 @@ inline static void BlurVertical( const std::vector& mesh, std::vector& smoothed ) { - //SCOPED_TIMER("Sim::SmoothHeightMesh::BlurVertical"); + // See BlurHorizontal for all the detailed comments. + const int lineSize = mapSize.x; const int mapMaxY = mapSize.y - 1; - //for_mt(min.x, max.x+1, [&](const int x) for (int x = min.x; x <= max.x; ++x) { float avg = 0.0f; @@ -368,42 +347,36 @@ inline static void BlurVertical( for (int y1 = li; y1 <= ri; ++y1) { avg += mesh[x + std::max(0, std::min(y1, mapMaxY)) * lineSize]; } - // LOG("%s: starting average is: %f (%f) (w: %f)", __func__, avg, avg*weight, weight); - ri++; // ri points to the next value to add + ri++; + +#ifdef SMOOTH_MESH_DEBUG_BLUR + LOG("%s: starting average is: %f (%f) (w: %f)", __func__, avg, avg*weight, weight); +#endif for (int y = min.y; y <= max.y; ++y) { avg += (-lv) + rv; + const float gh = GetRealGroundHeight(x, y, resolution); + smoothed[x + y * lineSize] = std::max(gh, avg*weight); - //const float ghaw = CGround::GetHeightReal(x * resolution, y * resolution); - // const float ghaw = readMap->GetCornerHeightMapSynced()[(x + y*mapDims.mapxp1)*resolution]; - const float ghaw = GetGroundHeight(x, y, resolution); - - // LOG("%s: x: %d, y: %d, avg: %f (%f) (g: %f)", __func__, x, y, avg, avg*weight, ghaw); - - smoothed[x + y * lineSize] = std::max(ghaw, avg*weight); - - // #pragma message ("FIX ME") - // smoothed[x + y * lineSize] = std::clamp( - // smoothed[x + y * lineSize], - // readMap->GetCurrMinHeight(), - // readMap->GetCurrMaxHeight() - // ); - - // assert(smoothed[x + y * lineSize] <= readMap->GetCurrMaxHeight() ); - // assert(smoothed[x + y * lineSize] >= readMap->GetCurrMinHeight() ); +#ifndef NDEBUG + assert(smoothed[x + y * lineSize] <= readMap->GetCurrMaxHeight() ); + assert(smoothed[x + y * lineSize] >= readMap->GetCurrMinHeight() ); +#endif lv = mesh[ x + std::max(0, std::min(li, mapMaxY)) * lineSize]; rv = mesh[ x + std::min(ri, mapMaxY) * lineSize]; li++; ri++; - // LOG("%s: for next line -%f +%f", __func__, lv, rv); +#ifdef SMOOTH_MESH_DEBUG_BLUR + LOG("%s: x: %d, y: %d, avg: %f (%f) (g: %f)", __func__, x, y, avg, avg*weight, gh); + LOG("%s: for next line -%f +%f", __func__, lv, rv); +#endif } - }//); + } } - inline static void CheckInvariants( int y, int maxx, @@ -429,10 +402,11 @@ inline static void CheckInvariants( } } -void SmoothHeightMesh::OnMapDamage(int x1, int y1, int x2, int y2) { + +void SmoothHeightMesh::MapChanged(int x1, int y1, int x2, int y2) { const bool queueWasEmpty = meshDamageTrack.damageQueue[meshDamageTrack.activeBuffer].empty(); - const int res = resolution*samplesPerQuad; + const int res = resolution*SAMPLES_PER_QUAD; const int w = meshDamageTrack.width; const int h = meshDamageTrack.height; @@ -458,8 +432,7 @@ void SmoothHeightMesh::OnMapDamage(int x1, int y1, int x2, int y2) { } -void SmoothHeightMesh::UpdateSmoothMesh() -{ +void SmoothHeightMesh::UpdateSmoothMesh() { SCOPED_TIMER("Sim::SmoothHeightMesh::UpdateSmoothMesh"); const bool flushBuffer = !meshDamageTrack.activeBuffer; @@ -468,19 +441,23 @@ void SmoothHeightMesh::UpdateSmoothMesh() && meshDamageTrack.horizontalBlurQueue.empty() && meshDamageTrack.verticalBlurQueue.empty(); - // LOG("%s: flush buffer is %d; damage queue is %I64d; blur queue is %I64d", - // __func__, - // (int)flushBuffer, - // meshDamageTrack.damageQueue[flushBuffer].size(), - // meshDamageTrack.blurQueue.size() - // ); +#ifdef SMOOTH_MESH_DEBUG_GENERAL + LOG("%s: flush buffer is %d; damage queue is %I64d; blur queue is %I64d", + __func__, + (int)flushBuffer, + meshDamageTrack.damageQueue[flushBuffer].size(), + meshDamageTrack.blurQueue.size() + ); +#endif if (currentWorkloadComplete){ const bool activeBufferReady = !meshDamageTrack.damageQueue[activeBuffer].empty() && gs->frameNum >= meshDamageTrack.queueReleaseOnFrame; if (activeBufferReady) { meshDamageTrack.activeBuffer = !meshDamageTrack.activeBuffer; - // LOG("%s: opening new queue", __func__); +#ifdef SMOOTH_MESH_DEBUG_GENERAL + LOG("%s: opening new queue", __func__); +#endif } return; } @@ -493,11 +470,10 @@ void SmoothHeightMesh::UpdateSmoothMesh() if (updateMaxima) activeQueue = &meshDamageTrack.damageQueue[flushBuffer]; - else if (doHorizontalBlur) { + else if (doHorizontalBlur) activeQueue = &meshDamageTrack.horizontalBlurQueue; - } else { + else activeQueue = &meshDamageTrack.verticalBlurQueue; - } damagedAreaIndex = activeQueue->front(); activeQueue->pop(); @@ -512,8 +488,8 @@ void SmoothHeightMesh::UpdateSmoothMesh() // area of map damaged. // area of the map which may need a max height change - int2 damageMin{damageX*samplesPerQuad, damageY*samplesPerQuad}; - int2 damageMax = damageMin + int2{samplesPerQuad - 1, samplesPerQuad - 1}; + int2 damageMin{damageX*SAMPLES_PER_QUAD, damageY*SAMPLES_PER_QUAD}; + int2 damageMax = damageMin + int2{SAMPLES_PER_QUAD - 1, SAMPLES_PER_QUAD - 1}; // area of map to load maximums for to update the updateLocation int2 min = damageMin - impactRadius; @@ -531,21 +507,23 @@ void SmoothHeightMesh::UpdateSmoothMesh() if (updateMaxima) { - // LOG("%s: quad index %d (%d,%d)-(%d,%d) (%d,%d)-(%d,%d) updating maxima", - // __func__, - // damagedAreaIndex, - // damageMin.x, damageMin.y, - // damageMax.x, damageMax.y, - // min.x, min.y, - // max.x, max.y - // ); - - // LOG("%s: quad area in world space (%f,%f) (%f,%f)", __func__ - // , (float)damageMin.x * fresolution - // , (float)damageMin.y * fresolution - // , (float)damageMax.x * fresolution - // , (float)damageMax.y * fresolution - // ); +#ifdef SMOOTH_MESH_DEBUG_GENERAL + LOG("%s: quad index %d (%d,%d)-(%d,%d) (%d,%d)-(%d,%d) updating maxima", + __func__, + damagedAreaIndex, + damageMin.x, damageMin.y, + damageMax.x, damageMax.y, + min.x, min.y, + max.x, max.y + ); + + LOG("%s: quad area in world space (%f,%f) (%f,%f)", __func__ + , (float)damageMin.x * fresolution + , (float)damageMin.y * fresolution + , (float)damageMax.x * fresolution + , (float)damageMax.y * fresolution + ); +#endif for (int i = min.x; i <= max.x; ++i) colsMaxima[i] = -std::numeric_limits::max(); @@ -557,7 +535,7 @@ void SmoothHeightMesh::UpdateSmoothMesh() for (int y = damageMin.y; y <= damageMax.y; ++y) { FindRadialMaximum(map, y, damageMin.x, damageMax.x, winSize, resolution, colsMaxima, maximaMesh); - AdvanceMaxima(map, y+1, min.x, max.x, winSize, resolution, colsMaxima, maximaRows); + AdvanceMaximas(map, y+1, min.x, max.x, winSize, resolution, colsMaxima, maximaRows); } //std::copy(maximaMesh.begin(), maximaMesh.end(), mesh.begin()); @@ -565,24 +543,24 @@ void SmoothHeightMesh::UpdateSmoothMesh() meshDamageTrack.damageMap[damagedAreaIndex] = false; } else { - // LOG("%s: quad index %d (%d,%d)-(%d,%d) (%d,%d)-(%d,%d) applying blur", - // __func__, - // damagedAreaIndex, - // damageMin.x, damageMin.y, - // damageMax.x, damageMax.y, - // min.x, min.y, - // max.x, max.y - // ); - - // LOG("%s: quad area in world space (%f,%f) (%f,%f)", __func__ - // , (float)damageMin.x * fresolution - // , (float)damageMin.y * fresolution - // , (float)damageMax.x * fresolution - // , (float)damageMax.y * fresolution - // ); - - // BlurHorizontal(map, damageMin, damageMax, blurSize, resolution, maximaMesh, mesh); - // BlurVertical(map, damageMin, damageMax, blurSize, resolution, maximaMesh, mesh); +#ifdef SMOOTH_MESH_DEBUG_GENERAL + LOG("%s: quad index %d (%d,%d)-(%d,%d) (%d,%d)-(%d,%d) applying blur", + __func__, + damagedAreaIndex, + damageMin.x, damageMin.y, + damageMax.x, damageMax.y, + min.x, min.y, + max.x, max.y + ); + + LOG("%s: quad area in world space (%f,%f) (%f,%f)", __func__ + , (float)damageMin.x * fresolution + , (float)damageMin.y * fresolution + , (float)damageMax.x * fresolution + , (float)damageMax.y * fresolution + ); +#endif + if (doHorizontalBlur) { BlurHorizontal(map, damageMin, damageMax, blurSize, resolution, maximaMesh, tempMesh); meshDamageTrack.verticalBlurQueue.push(damagedAreaIndex); @@ -597,73 +575,38 @@ void SmoothHeightMesh::UpdateSmoothMesh() } } } - - // std::copy(maximaMesh.begin(), maximaMesh.end(), mesh.begin()); } -void SmoothHeightMesh::MakeSmoothMesh() -{ - ScopedOnceTimer timer("SmoothHeightMesh::MakeSmoothMesh"); - - // info: - // height-value array has size * - // and represents a grid of cols by rows - // maximum legal index is ((maxx + 1) * (maxy + 1)) - 1 - // - // row-width (number of height-value corners per row) is (maxx + 1) - // col-height (number of height-value corners per col) is (maxy + 1) - // - // 1st row has indices [maxx*( 0) + ( 0), maxx*(1) + ( 0)] inclusive - // 2nd row has indices [maxx*( 1) + ( 1), maxx*(2) + ( 1)] inclusive - // 3rd row has indices [maxx*( 2) + ( 2), maxx*(3) + ( 2)] inclusive - // ... - // Nth row has indices [maxx*(N-1) + (N-1), maxx*(N) + (N-1)] inclusive - // - // use sliding window of maximums to reduce computational complexity - const int winSize = smoothRadius / resolution; - const int blurSize = std::max(1, winSize / 2); - - // const auto fillGaussianKernelFunc = [blurSize](std::vector& gaussianKernel, const float sigma) { - // gaussianKernel.resize(blurSize + 1); - - // const auto gaussianG = [](const int x, const float sigma) -> float { - // // 0.3989422804f = 1/sqrt(2*pi) - // return 0.3989422804f * math::expf(-0.5f * x * x / (sigma * sigma)) / sigma; - // }; - - // float sum = (gaussianKernel[0] = gaussianG(0, sigma)); - - // for (int i = 1; i < blurSize + 1; ++i) { - // sum += 2.0f * (gaussianKernel[i] = gaussianG(i, sigma)); - // } - - // for (auto& gk : gaussianKernel) { - // gk /= sum; - // } - // }; - - // constexpr float gSigma = 5.0f; - // fillGaussianKernelFunc(gaussianKernel, gSigma); - - - const int damageTrackWidth = maxx / samplesPerQuad + (maxx % samplesPerQuad ? 1 : 0); - const int damageTrackHeight = maxy / samplesPerQuad + (maxy % samplesPerQuad ? 1 : 0); +void SmoothHeightMesh::InitMapChangeTracking() { + const int damageTrackWidth = maxx / SAMPLES_PER_QUAD + (maxx % SAMPLES_PER_QUAD ? 1 : 0); + const int damageTrackHeight = maxy / SAMPLES_PER_QUAD + (maxy % SAMPLES_PER_QUAD ? 1 : 0); const int damageTrackQuads = damageTrackWidth * damageTrackHeight; meshDamageTrack.damageMap.clear(); meshDamageTrack.damageMap.resize(damageTrackQuads); meshDamageTrack.width = damageTrackWidth; meshDamageTrack.height = damageTrackHeight; +} +void SmoothHeightMesh::InitDataStructures() { assert(mesh.empty()); - maximaMesh.resize((maxx) * (maxy), 0.0f); - mesh.resize((maxx) * (maxy), 0.0f); - tempMesh.resize((maxx) * (maxy), 0.0f); - origMesh.resize((maxx) * (maxy), 0.0f); + maximaMesh.resize(maxx * maxy, 0.0f); + mesh.resize(maxx * maxy, 0.0f); + tempMesh.resize(maxx * maxy, 0.0f); + origMesh.resize(maxx * maxy, 0.0f); colsMaxima.clear(); colsMaxima.resize(maxx, -std::numeric_limits::max()); maximaRows.clear(); maximaRows.resize(maxx, -1); +} + +void SmoothHeightMesh::MakeSmoothMesh() { + ScopedOnceTimer timer("SmoothHeightMesh::MakeSmoothMesh"); + // A sliding window is used to reduce computational complexity + const int winSize = smoothRadius / resolution; + + // blur size is half the window size to create a wider plateau + const int blurSize = std::max(1, winSize / 2); int2 min{0, 0}; int2 max{maxx-1, maxy-1}; int2 map{maxx, maxy}; @@ -671,28 +614,47 @@ void SmoothHeightMesh::MakeSmoothMesh() FindMaximumColumnHeights(map, 0, 0, max.x, winSize, resolution, colsMaxima, maximaRows); for (int y = 0; y <= max.y; ++y) { - //AdvanceMaximaRows(y, 0, max.x, resolution, colsMaxima, maximaRows); FindRadialMaximum(map, y, 0, max.x, winSize, resolution, colsMaxima, maximaMesh); - AdvanceMaxima(map, y+1, 0, max.x, winSize, resolution, colsMaxima, maximaRows); + AdvanceMaximas(map, y+1, 0, max.x, winSize, resolution, colsMaxima, maximaRows); -// #ifdef _DEBUG -// CheckInvariants(y, maxx, maxy, winSize, resolution, colsMaxima, maximaRows); -// #endif +#ifdef _DEBUG + CheckInvariants(y, maxx, maxy, winSize, fresolution, colsMaxima, maximaRows); +#endif } - // actually smooth with approximate Gaussian blur passes BlurHorizontal(map, min, max, blurSize, resolution, maximaMesh, tempMesh); BlurVertical(map, min, max, blurSize, resolution, tempMesh, mesh); - // int i = 0; - // for (int y =0; y < maxy; y++) - // for (int x = 0; x < maxx; x++) { - // maximaMesh[i++] = readMap->GetCornerHeightMapSynced()[(x + y*mapDims.mapxp1)*resolution]; - // } - // now contains the final smoothed heightmap, save it in origMesh std::copy(mesh.begin(), mesh.end(), origMesh.begin()); + + // tempMesh should be kept inline with mesh to avoid bluring artefacts in dynamic updates std::copy(mesh.begin(), mesh.end(), tempMesh.begin()); - // std::copy(maximaMesh.begin(), maximaMesh.end(), origMesh.begin()); - // std::copy(maximaMesh.begin(), maximaMesh.end(), mesh.begin()); } + + +// ============================================= +// Old gaussian kernel reference code kept here for future reference +// ============================================= + // const auto fillGaussianKernelFunc = [blurSize](std::vector& gaussianKernel, const float sigma) { + // gaussianKernel.resize(blurSize + 1); + + // const auto gaussianG = [](const int x, const float sigma) -> float { + // // 0.3989422804f = 1/sqrt(2*pi) + // return 0.3989422804f * math::expf(-0.5f * x * x / (sigma * sigma)) / sigma; + // }; + + // float sum = (gaussianKernel[0] = gaussianG(0, sigma)); + + // for (int i = 1; i < blurSize + 1; ++i) { + // sum += 2.0f * (gaussianKernel[i] = gaussianG(i, sigma)); + // } + + // for (auto& gk : gaussianKernel) { + // gk /= sum; + // } + // }; + + // constexpr float gSigma = 5.0f; + // fillGaussianKernelFunc(gaussianKernel, gSigma); +// ============================================= diff --git a/rts/Sim/Misc/SmoothHeightMesh.h b/rts/Sim/Misc/SmoothHeightMesh.h index 53e4264fa4..6c221003bf 100644 --- a/rts/Sim/Misc/SmoothHeightMesh.h +++ b/rts/Sim/Misc/SmoothHeightMesh.h @@ -11,23 +11,23 @@ class CGround; -struct DamageMesh { - std::vector damageMap; - std::queue damageQueue[2]; - std::queue horizontalBlurQueue; - std::queue verticalBlurQueue; - int width = 0; - int height = 0; - int queueReleaseOnFrame = 0; - bool activeBuffer = 0; -}; - /** * Provides a GetHeight(x, y) of its own that smooths the mesh. */ class SmoothHeightMesh { public: + struct MapChangeTrack { + std::vector damageMap; + std::queue damageQueue[2]; + std::queue horizontalBlurQueue; + std::queue verticalBlurQueue; + int width = 0; + int height = 0; + int queueReleaseOnFrame = 0; + bool activeBuffer = 0; + }; + void Init(int2 max, int res, int smoothRad); void Kill(); @@ -48,11 +48,14 @@ class SmoothHeightMesh void UpdateSmoothMesh(); - void OnMapDamage(int x1, int z1, int x2, int z2); + void MapChanged(int x1, int z1, int x2, int z2); private: + void InitMapChangeTracking(); + void InitDataStructures(); void MakeSmoothMesh(); + int maxx = 0; int maxy = 0; float fmaxx = 0.0f; @@ -69,7 +72,7 @@ class SmoothHeightMesh std::vector colsMaxima; std::vector maximaRows; - DamageMesh meshDamageTrack; + MapChangeTrack meshDamageTrack; void UpdateMapMaximaGrid(); void BuildNewMapMaximaGrid(); From 841e1f639e8eb830613e2ba6ecda6ed6eebc4840 Mon Sep 17 00:00:00 2001 From: Marcus Hutchings Date: Tue, 31 May 2022 12:34:19 +0100 Subject: [PATCH 10/20] Added smooth mesh debug to show damage due to be processed on the minimap. --- rts/Rendering/SmoothHeightMeshDrawer.cpp | 76 ++++++++++++++++++++++-- rts/Rendering/SmoothHeightMeshDrawer.h | 15 ++++- rts/Sim/Misc/SmoothHeightMesh.cpp | 75 ++++++++++++----------- rts/Sim/Misc/SmoothHeightMesh.h | 11 +++- 4 files changed, 133 insertions(+), 44 deletions(-) diff --git a/rts/Rendering/SmoothHeightMeshDrawer.cpp b/rts/Rendering/SmoothHeightMeshDrawer.cpp index ff5575f20b..25a2623ab7 100644 --- a/rts/Rendering/SmoothHeightMeshDrawer.cpp +++ b/rts/Rendering/SmoothHeightMeshDrawer.cpp @@ -1,18 +1,86 @@ /* This file is part of the Spring engine (GPL v2 or later), see LICENSE.html */ #include "SmoothHeightMeshDrawer.h" -#include "Sim/Misc/SmoothHeightMesh.h" + +#include "Game/UI/MiniMap.h" +#include "Map/ReadMap.h" +#include "Rendering/GL/myGL.h" #include "Rendering/GL/VertexArray.h" +#include "Sim/Misc/GlobalSynced.h" +#include "Sim/Misc/SmoothHeightMesh.h" +#include "System/EventHandler.h" #include "System/float3.h" +using namespace SmoothHeightMeshNamespace; + +static SmoothHeightMeshDrawer* smoothMeshDrawer = nullptr; SmoothHeightMeshDrawer* SmoothHeightMeshDrawer::GetInstance() { - static SmoothHeightMeshDrawer drawer; - return &drawer; + if (smoothMeshDrawer == nullptr) { + smoothMeshDrawer = new SmoothHeightMeshDrawer(); + } + return smoothMeshDrawer; +} + +void SmoothHeightMeshDrawer::FreeInstance() { + if (smoothMeshDrawer != nullptr) { + delete smoothMeshDrawer; + smoothMeshDrawer = nullptr; + } +} + +SmoothHeightMeshDrawer::SmoothHeightMeshDrawer() + : CEventClient("[SmoothHeightMeshDrawer]", 300002, false) + , drawEnabled(false) +{ + eventHandler.AddClient(this); +} +SmoothHeightMeshDrawer::~SmoothHeightMeshDrawer() { + eventHandler.RemoveClient(this); +} + +void SmoothHeightMeshDrawer::DrawInMiniMap() +{ + if (!(drawEnabled && gs->cheatEnabled)) + return; + + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadIdentity(); + glOrtho(0.0f, 1.0f, 0.0f, 1.0f, 0.0, -1.0); + minimap->ApplyConstraintsMatrix(); + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity(); + glTranslatef3(UpVector); + glScalef(1.0f / mapDims.mapx, -1.0f / mapDims.mapy, 1.0f); + + glDisable(GL_TEXTURE_2D); + glColor4f(1.0f, 1.0f, 0.0f, 0.7f); + + const SmoothHeightMesh::MapChangeTrack& mapChangeTrack = smoothGround.mapChangeTrack; + const float tileSize = SAMPLES_PER_QUAD * smoothGround.resolution; + int i = 0; + for (auto changed : mapChangeTrack.damageMap) { + if (changed){ + const float x = (i % mapChangeTrack.width) * tileSize; + const float y = (i / mapChangeTrack.width) * tileSize; + glRectf(x, y, x + tileSize, y + tileSize); + } + i++; + } + + glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + glEnable(GL_TEXTURE_2D); + + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + glMatrixMode(GL_MODELVIEW); + glPopMatrix(); } void SmoothHeightMeshDrawer::Draw(float yoffset) { - if (!drawEnabled) + if (!(drawEnabled && gs->cheatEnabled)) return; glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); diff --git a/rts/Rendering/SmoothHeightMeshDrawer.h b/rts/Rendering/SmoothHeightMeshDrawer.h index c75df90d97..062be10cd1 100644 --- a/rts/Rendering/SmoothHeightMeshDrawer.h +++ b/rts/Rendering/SmoothHeightMeshDrawer.h @@ -3,9 +3,22 @@ #ifndef SMOOTH_HEIGHTMESH_DRAWER_H #define SMOOTH_HEIGHTMESH_DRAWER_H -struct SmoothHeightMeshDrawer { +#include "System/EventClient.h" + +struct SmoothHeightMeshDrawer: public CEventClient { public: static SmoothHeightMeshDrawer* GetInstance(); + static void FreeInstance(); + + SmoothHeightMeshDrawer(); + ~SmoothHeightMeshDrawer(); + + // CEventClient interface + bool WantsEvent(const std::string& eventName) override { + return (eventName == "DrawInMiniMap"); + } + void DrawInMiniMap() override; + void Draw(float yoffset); bool& DrawEnabled() { return drawEnabled; } diff --git a/rts/Sim/Misc/SmoothHeightMesh.cpp b/rts/Sim/Misc/SmoothHeightMesh.cpp index 85a9435859..f665d08030 100644 --- a/rts/Sim/Misc/SmoothHeightMesh.cpp +++ b/rts/Sim/Misc/SmoothHeightMesh.cpp @@ -15,6 +15,8 @@ #include "System/Log/ILog.h" +using namespace SmoothHeightMeshNamespace; + #if 0 #define SMOOTH_MESH_DEBUG_MAXIMA #endif @@ -27,9 +29,6 @@ #define SMOOTH_MESH_DEBUG_GENERAL #endif -constexpr int SMOOTH_MESH_UPDATE_DELAY = GAME_SPEED; -constexpr int SAMPLES_PER_QUAD = 32; - SmoothHeightMesh smoothGround; @@ -78,12 +77,12 @@ void SmoothHeightMesh::Init(int2 max, int res, int smoothRad) } void SmoothHeightMesh::Kill() { - while (!meshDamageTrack.damageQueue[0].empty()) { meshDamageTrack.damageQueue[0].pop(); } - while (!meshDamageTrack.damageQueue[1].empty()) { meshDamageTrack.damageQueue[1].pop(); } - while (!meshDamageTrack.horizontalBlurQueue.empty()) { meshDamageTrack.horizontalBlurQueue.pop(); } - while (!meshDamageTrack.verticalBlurQueue.empty()) { meshDamageTrack.verticalBlurQueue.pop(); } + while (!mapChangeTrack.damageQueue[0].empty()) { mapChangeTrack.damageQueue[0].pop(); } + while (!mapChangeTrack.damageQueue[1].empty()) { mapChangeTrack.damageQueue[1].pop(); } + while (!mapChangeTrack.horizontalBlurQueue.empty()) { mapChangeTrack.horizontalBlurQueue.pop(); } + while (!mapChangeTrack.verticalBlurQueue.empty()) { mapChangeTrack.verticalBlurQueue.pop(); } - meshDamageTrack.damageMap.clear(); + mapChangeTrack.damageMap.clear(); maximaMesh.clear(); mesh.clear(); origMesh.clear(); @@ -405,10 +404,10 @@ inline static void CheckInvariants( void SmoothHeightMesh::MapChanged(int x1, int y1, int x2, int y2) { - const bool queueWasEmpty = meshDamageTrack.damageQueue[meshDamageTrack.activeBuffer].empty(); + const bool queueWasEmpty = mapChangeTrack.damageQueue[mapChangeTrack.activeBuffer].empty(); const int res = resolution*SAMPLES_PER_QUAD; - const int w = meshDamageTrack.width; - const int h = meshDamageTrack.height; + const int w = mapChangeTrack.width; + const int h = mapChangeTrack.height; const int2 min { std::max((x1 - smoothRadius) / res, 0) , std::max((y1 - smoothRadius) / res, 0)}; @@ -418,28 +417,28 @@ void SmoothHeightMesh::MapChanged(int x1, int y1, int x2, int y2) { for (int y = min.y; y <= max.y; ++y) { int i = min.x + y*w; for (int x = min.x; x <= max.x; ++x, ++i) { - if (!meshDamageTrack.damageMap[i]) { - meshDamageTrack.damageMap[i] = true; - meshDamageTrack.damageQueue[meshDamageTrack.activeBuffer].push(i); + if (!mapChangeTrack.damageMap[i]) { + mapChangeTrack.damageMap[i] = true; + mapChangeTrack.damageQueue[mapChangeTrack.activeBuffer].push(i); } } } - const bool queueWasUpdated = !meshDamageTrack.damageQueue[meshDamageTrack.activeBuffer].empty(); + const bool queueWasUpdated = !mapChangeTrack.damageQueue[mapChangeTrack.activeBuffer].empty(); if (queueWasEmpty && queueWasUpdated) - meshDamageTrack.queueReleaseOnFrame = gs->frameNum + SMOOTH_MESH_UPDATE_DELAY; + mapChangeTrack.queueReleaseOnFrame = gs->frameNum + SMOOTH_MESH_UPDATE_DELAY; } void SmoothHeightMesh::UpdateSmoothMesh() { SCOPED_TIMER("Sim::SmoothHeightMesh::UpdateSmoothMesh"); - const bool flushBuffer = !meshDamageTrack.activeBuffer; - const bool activeBuffer = meshDamageTrack.activeBuffer; - const bool currentWorkloadComplete = meshDamageTrack.damageQueue[flushBuffer].empty() - && meshDamageTrack.horizontalBlurQueue.empty() - && meshDamageTrack.verticalBlurQueue.empty(); + const bool flushBuffer = !mapChangeTrack.activeBuffer; + const bool activeBuffer = mapChangeTrack.activeBuffer; + const bool currentWorkloadComplete = mapChangeTrack.damageQueue[flushBuffer].empty() + && mapChangeTrack.horizontalBlurQueue.empty() + && mapChangeTrack.verticalBlurQueue.empty(); #ifdef SMOOTH_MESH_DEBUG_GENERAL LOG("%s: flush buffer is %d; damage queue is %I64d; blur queue is %I64d", @@ -451,10 +450,10 @@ void SmoothHeightMesh::UpdateSmoothMesh() { #endif if (currentWorkloadComplete){ - const bool activeBufferReady = !meshDamageTrack.damageQueue[activeBuffer].empty() - && gs->frameNum >= meshDamageTrack.queueReleaseOnFrame; + const bool activeBufferReady = !mapChangeTrack.damageQueue[activeBuffer].empty() + && gs->frameNum >= mapChangeTrack.queueReleaseOnFrame; if (activeBufferReady) { - meshDamageTrack.activeBuffer = !meshDamageTrack.activeBuffer; + mapChangeTrack.activeBuffer = !mapChangeTrack.activeBuffer; #ifdef SMOOTH_MESH_DEBUG_GENERAL LOG("%s: opening new queue", __func__); #endif @@ -462,18 +461,18 @@ void SmoothHeightMesh::UpdateSmoothMesh() { return; } - bool updateMaxima = !meshDamageTrack.damageQueue[flushBuffer].empty(); - bool doHorizontalBlur = !meshDamageTrack.horizontalBlurQueue.empty(); + bool updateMaxima = !mapChangeTrack.damageQueue[flushBuffer].empty(); + bool doHorizontalBlur = !mapChangeTrack.horizontalBlurQueue.empty(); int damagedAreaIndex = 0; std::queue* activeQueue = nullptr; if (updateMaxima) - activeQueue = &meshDamageTrack.damageQueue[flushBuffer]; + activeQueue = &mapChangeTrack.damageQueue[flushBuffer]; else if (doHorizontalBlur) - activeQueue = &meshDamageTrack.horizontalBlurQueue; + activeQueue = &mapChangeTrack.horizontalBlurQueue; else - activeQueue = &meshDamageTrack.verticalBlurQueue; + activeQueue = &mapChangeTrack.verticalBlurQueue; damagedAreaIndex = activeQueue->front(); activeQueue->pop(); @@ -483,8 +482,8 @@ void SmoothHeightMesh::UpdateSmoothMesh() { int2 impactRadius{winSize, winSize}; - const int damageX = damagedAreaIndex % meshDamageTrack.width; - const int damageY = damagedAreaIndex / meshDamageTrack.width; + const int damageX = damagedAreaIndex % mapChangeTrack.width; + const int damageY = damagedAreaIndex / mapChangeTrack.width; // area of map damaged. // area of the map which may need a max height change @@ -539,8 +538,8 @@ void SmoothHeightMesh::UpdateSmoothMesh() { } //std::copy(maximaMesh.begin(), maximaMesh.end(), mesh.begin()); - meshDamageTrack.horizontalBlurQueue.push(damagedAreaIndex); - meshDamageTrack.damageMap[damagedAreaIndex] = false; + mapChangeTrack.horizontalBlurQueue.push(damagedAreaIndex); + mapChangeTrack.damageMap[damagedAreaIndex] = false; } else { #ifdef SMOOTH_MESH_DEBUG_GENERAL @@ -563,7 +562,7 @@ void SmoothHeightMesh::UpdateSmoothMesh() { if (doHorizontalBlur) { BlurHorizontal(map, damageMin, damageMax, blurSize, resolution, maximaMesh, tempMesh); - meshDamageTrack.verticalBlurQueue.push(damagedAreaIndex); + mapChangeTrack.verticalBlurQueue.push(damagedAreaIndex); } else { BlurVertical(map, damageMin, damageMax, blurSize, resolution, tempMesh, mesh); @@ -581,10 +580,10 @@ void SmoothHeightMesh::InitMapChangeTracking() { const int damageTrackWidth = maxx / SAMPLES_PER_QUAD + (maxx % SAMPLES_PER_QUAD ? 1 : 0); const int damageTrackHeight = maxy / SAMPLES_PER_QUAD + (maxy % SAMPLES_PER_QUAD ? 1 : 0); const int damageTrackQuads = damageTrackWidth * damageTrackHeight; - meshDamageTrack.damageMap.clear(); - meshDamageTrack.damageMap.resize(damageTrackQuads); - meshDamageTrack.width = damageTrackWidth; - meshDamageTrack.height = damageTrackHeight; + mapChangeTrack.damageMap.clear(); + mapChangeTrack.damageMap.resize(damageTrackQuads); + mapChangeTrack.width = damageTrackWidth; + mapChangeTrack.height = damageTrackHeight; } void SmoothHeightMesh::InitDataStructures() { diff --git a/rts/Sim/Misc/SmoothHeightMesh.h b/rts/Sim/Misc/SmoothHeightMesh.h index 6c221003bf..322091d8a4 100644 --- a/rts/Sim/Misc/SmoothHeightMesh.h +++ b/rts/Sim/Misc/SmoothHeightMesh.h @@ -7,16 +7,25 @@ #include #include +#include "Sim/Misc/GlobalConstants.h" #include "System/type2.h" class CGround; +namespace SmoothHeightMeshNamespace { + constexpr int SMOOTH_MESH_UPDATE_DELAY = GAME_SPEED; + constexpr int SAMPLES_PER_QUAD = 32; +} + /** * Provides a GetHeight(x, y) of its own that smooths the mesh. */ class SmoothHeightMesh { + friend class SmoothHeightMeshDrawer; + public: + struct MapChangeTrack { std::vector damageMap; std::queue damageQueue[2]; @@ -72,7 +81,7 @@ class SmoothHeightMesh std::vector colsMaxima; std::vector maximaRows; - MapChangeTrack meshDamageTrack; + MapChangeTrack mapChangeTrack; void UpdateMapMaximaGrid(); void BuildNewMapMaximaGrid(); From b1fbeef193662d27f6afa0a5a5fdb0dc1db7e0db Mon Sep 17 00:00:00 2001 From: Marcus Hutchings Date: Tue, 31 May 2022 15:33:51 +0100 Subject: [PATCH 11/20] Fixed Height above water bug. --- rts/Sim/Misc/SmoothHeightMesh.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/rts/Sim/Misc/SmoothHeightMesh.cpp b/rts/Sim/Misc/SmoothHeightMesh.cpp index f665d08030..a15303d26f 100644 --- a/rts/Sim/Misc/SmoothHeightMesh.cpp +++ b/rts/Sim/Misc/SmoothHeightMesh.cpp @@ -34,10 +34,10 @@ SmoothHeightMesh smoothGround; static float Interpolate(float x, float y, const int maxx, const int maxy, const float res, const float* heightmap) { - x = Clamp(x / res, 0.0f, ((float)maxx - 1)); - y = Clamp(y / res, 0.0f, ((float)maxy - 1)); - const int sx = x; - const int sy = y; + x = Clamp(x / res, 0.0f, (float)maxx); + y = Clamp(y / res, 0.0f, (float)maxy); + const int sx = std::min((int)x, maxx - 1); + const int sy = std::min((int)y, maxy - 1); const float dx = (x - sx); const float dy = (y - sy); @@ -97,7 +97,7 @@ float SmoothHeightMesh::GetHeight(float x, float y) float SmoothHeightMesh::GetHeightAboveWater(float x, float y) { assert(!mesh.empty()); - return std::max(0.0f, Interpolate(x, y, maxx, maxy, resolution, &mesh[0])); + return std::max(0.0f, Interpolate(x, y, maxx, maxy, fresolution, &mesh[0])); } float SmoothHeightMesh::SetHeight(int index, float h) From 78d529315267d245cdf86cc66034b8022ab91524 Mon Sep 17 00:00:00 2001 From: Marcus Hutchings Date: Tue, 31 May 2022 15:47:54 +0100 Subject: [PATCH 12/20] Refactored part of mesh copying. --- rts/Sim/Misc/SmoothHeightMesh.cpp | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/rts/Sim/Misc/SmoothHeightMesh.cpp b/rts/Sim/Misc/SmoothHeightMesh.cpp index a15303d26f..ddb3bc71d6 100644 --- a/rts/Sim/Misc/SmoothHeightMesh.cpp +++ b/rts/Sim/Misc/SmoothHeightMesh.cpp @@ -402,6 +402,21 @@ inline static void CheckInvariants( } +inline static void CopyMeshPart + ( int mapx + , int2 min + , int2 max + , const std::vector& src + , std::vector& dest + ) { + for (int y = min.y; y <= max.y; ++y) { + const int startIdx = min.x + y*mapx; + const int endIdx = max.x + y*mapx + 1; + std::copy(&src[startIdx], &src[endIdx], &dest[startIdx]); + } +} + + void SmoothHeightMesh::MapChanged(int x1, int y1, int x2, int y2) { const bool queueWasEmpty = mapChangeTrack.damageQueue[mapChangeTrack.activeBuffer].empty(); @@ -536,7 +551,6 @@ void SmoothHeightMesh::UpdateSmoothMesh() { FindRadialMaximum(map, y, damageMin.x, damageMax.x, winSize, resolution, colsMaxima, maximaMesh); AdvanceMaximas(map, y+1, min.x, max.x, winSize, resolution, colsMaxima, maximaRows); } - //std::copy(maximaMesh.begin(), maximaMesh.end(), mesh.begin()); mapChangeTrack.horizontalBlurQueue.push(damagedAreaIndex); mapChangeTrack.damageMap[damagedAreaIndex] = false; @@ -566,12 +580,7 @@ void SmoothHeightMesh::UpdateSmoothMesh() { } else { BlurVertical(map, damageMin, damageMax, blurSize, resolution, tempMesh, mesh); - - for (int y = damageMin.y; y <= damageMax.y; ++y) { - const int startIdx = damageMin.x + y*map.x; - const int endIdx = damageMax.x + y*map.x + 1; - std::copy(&mesh[startIdx], &mesh[endIdx], &tempMesh[startIdx]); - } + CopyMeshPart(map.x, damageMin, damageMax, mesh, tempMesh); } } } From 058e416455e7daa65c7f6c557cd39f7260c99290 Mon Sep 17 00:00:00 2001 From: Marcus Hutchings Date: Tue, 31 May 2022 16:35:30 +0100 Subject: [PATCH 13/20] code refactoring. --- rts/Sim/Misc/SmoothHeightMesh.cpp | 222 +++++++++++++++--------------- rts/Sim/Misc/SmoothHeightMesh.h | 2 +- 2 files changed, 109 insertions(+), 115 deletions(-) diff --git a/rts/Sim/Misc/SmoothHeightMesh.cpp b/rts/Sim/Misc/SmoothHeightMesh.cpp index ddb3bc71d6..47d25cdfaa 100644 --- a/rts/Sim/Misc/SmoothHeightMesh.cpp +++ b/rts/Sim/Misc/SmoothHeightMesh.cpp @@ -76,6 +76,28 @@ void SmoothHeightMesh::Init(int2 max, int res, int smoothRad) MakeSmoothMesh(); } +void SmoothHeightMesh::InitMapChangeTracking() { + const int damageTrackWidth = maxx / SAMPLES_PER_QUAD + (maxx % SAMPLES_PER_QUAD ? 1 : 0); + const int damageTrackHeight = maxy / SAMPLES_PER_QUAD + (maxy % SAMPLES_PER_QUAD ? 1 : 0); + const int damageTrackQuads = damageTrackWidth * damageTrackHeight; + mapChangeTrack.damageMap.clear(); + mapChangeTrack.damageMap.resize(damageTrackQuads); + mapChangeTrack.width = damageTrackWidth; + mapChangeTrack.height = damageTrackHeight; +} + +void SmoothHeightMesh::InitDataStructures() { + assert(mesh.empty()); + maximaMesh.resize(maxx * maxy, 0.0f); + mesh.resize(maxx * maxy, 0.0f); + tempMesh.resize(maxx * maxy, 0.0f); + origMesh.resize(maxx * maxy, 0.0f); + colsMaxima.clear(); + colsMaxima.resize(maxx, -std::numeric_limits::max()); + maximaRows.clear(); + maximaRows.resize(maxx, -1); +} + void SmoothHeightMesh::Kill() { while (!mapChangeTrack.damageQueue[0].empty()) { mapChangeTrack.damageQueue[0].pop(); } while (!mapChangeTrack.damageQueue[1].empty()) { mapChangeTrack.damageQueue[1].pop(); } @@ -402,21 +424,6 @@ inline static void CheckInvariants( } -inline static void CopyMeshPart - ( int mapx - , int2 min - , int2 max - , const std::vector& src - , std::vector& dest - ) { - for (int y = min.y; y <= max.y; ++y) { - const int startIdx = min.x + y*mapx; - const int endIdx = max.x + y*mapx + 1; - std::copy(&src[startIdx], &src[endIdx], &dest[startIdx]); - } -} - - void SmoothHeightMesh::MapChanged(int x1, int y1, int x2, int y2) { const bool queueWasEmpty = mapChangeTrack.damageQueue[mapChangeTrack.activeBuffer].empty(); @@ -446,9 +453,22 @@ void SmoothHeightMesh::MapChanged(int x1, int y1, int x2, int y2) { } -void SmoothHeightMesh::UpdateSmoothMesh() { - SCOPED_TIMER("Sim::SmoothHeightMesh::UpdateSmoothMesh"); +inline static void CopyMeshPart + ( int mapx + , int2 min + , int2 max + , const std::vector& src + , std::vector& dest + ) { + for (int y = min.y; y <= max.y; ++y) { + const int startIdx = min.x + y*mapx; + const int endIdx = max.x + y*mapx + 1; + std::copy(&src[startIdx], &src[endIdx], &dest[startIdx]); + } +} + +inline static bool UpdateSmoothMeshRequired(SmoothHeightMesh::MapChangeTrack& mapChangeTrack) { const bool flushBuffer = !mapChangeTrack.activeBuffer; const bool activeBuffer = mapChangeTrack.activeBuffer; const bool currentWorkloadComplete = mapChangeTrack.damageQueue[flushBuffer].empty() @@ -456,11 +476,9 @@ void SmoothHeightMesh::UpdateSmoothMesh() { && mapChangeTrack.verticalBlurQueue.empty(); #ifdef SMOOTH_MESH_DEBUG_GENERAL - LOG("%s: flush buffer is %d; damage queue is %I64d; blur queue is %I64d", - __func__, - (int)flushBuffer, - meshDamageTrack.damageQueue[flushBuffer].size(), - meshDamageTrack.blurQueue.size() + LOG("%s: flush buffer is %d; damage queue is %I64d; blur queue is %I64d" + , __func__, (int)flushBuffer, meshDamageTrack.damageQueue[flushBuffer].size() + , meshDamageTrack.blurQueue.size() ); #endif @@ -473,105 +491,102 @@ void SmoothHeightMesh::UpdateSmoothMesh() { LOG("%s: opening new queue", __func__); #endif } - return; } - bool updateMaxima = !mapChangeTrack.damageQueue[flushBuffer].empty(); - bool doHorizontalBlur = !mapChangeTrack.horizontalBlurQueue.empty(); - int damagedAreaIndex = 0; - - std::queue* activeQueue = nullptr; - - if (updateMaxima) - activeQueue = &mapChangeTrack.damageQueue[flushBuffer]; - else if (doHorizontalBlur) - activeQueue = &mapChangeTrack.horizontalBlurQueue; - else - activeQueue = &mapChangeTrack.verticalBlurQueue; + return !currentWorkloadComplete; +} - damagedAreaIndex = activeQueue->front(); - activeQueue->pop(); +void SmoothHeightMesh::UpdateSmoothMeshMaximas(int2 damageMin, int2 damageMax) { const int winSize = smoothRadius / resolution; const int blurSize = std::max(1, winSize / 2); + int2 map{maxx, maxy}; int2 impactRadius{winSize, winSize}; - - const int damageX = damagedAreaIndex % mapChangeTrack.width; - const int damageY = damagedAreaIndex / mapChangeTrack.width; - - // area of map damaged. - // area of the map which may need a max height change - int2 damageMin{damageX*SAMPLES_PER_QUAD, damageY*SAMPLES_PER_QUAD}; - int2 damageMax = damageMin + int2{SAMPLES_PER_QUAD - 1, SAMPLES_PER_QUAD - 1}; - - // area of map to load maximums for to update the updateLocation int2 min = damageMin - impactRadius; int2 max = damageMax + impactRadius; - int2 map{maxx, maxy}; - damageMin.x = std::clamp(damageMin.x, 0, map.x); - damageMin.y = std::clamp(damageMin.y, 0, map.y); - damageMax.x = std::clamp(damageMax.x, 0, map.x); - damageMax.y = std::clamp(damageMax.y, 0, map.y); min.x = std::clamp(min.x, 0, map.x - 1); min.y = std::clamp(min.y, 0, map.y - 1); max.x = std::clamp(max.x, 0, map.x - 1); max.y = std::clamp(max.y, 0, map.y - 1); - if (updateMaxima) { - #ifdef SMOOTH_MESH_DEBUG_GENERAL - LOG("%s: quad index %d (%d,%d)-(%d,%d) (%d,%d)-(%d,%d) updating maxima", - __func__, - damagedAreaIndex, - damageMin.x, damageMin.y, - damageMax.x, damageMax.y, - min.x, min.y, - max.x, max.y - ); - - LOG("%s: quad area in world space (%f,%f) (%f,%f)", __func__ - , (float)damageMin.x * fresolution - , (float)damageMin.y * fresolution - , (float)damageMax.x * fresolution - , (float)damageMax.y * fresolution - ); +LOG("%s: quad index %d (%d,%d)-(%d,%d) (%d,%d)-(%d,%d) updating maxima" + , __func__, damagedAreaIndex, damageMin.x, damageMin.y, damageMax.x, damageMax.y, min.x, min.y, max.x, max.y + ); + +LOG("%s: quad area in world space (%f,%f) (%f,%f)", __func__ + , (float)damageMin.x * fresolution, (float)damageMin.y * fresolution + , (float)damageMax.x * fresolution, (float)damageMax.y * fresolution + ); #endif - for (int i = min.x; i <= max.x; ++i) - colsMaxima[i] = -std::numeric_limits::max(); + for (int i = min.x; i <= max.x; ++i) + colsMaxima[i] = -std::numeric_limits::max(); - for (int i = min.x; i <= max.x; ++i) - maximaRows[i] = -1; + for (int i = min.x; i <= max.x; ++i) + maximaRows[i] = -1; - FindMaximumColumnHeights(map, damageMin.y, min.x, max.x, winSize, resolution, colsMaxima, maximaRows); + FindMaximumColumnHeights(map, damageMin.y, min.x, max.x, winSize, resolution, colsMaxima, maximaRows); - for (int y = damageMin.y; y <= damageMax.y; ++y) { - FindRadialMaximum(map, y, damageMin.x, damageMax.x, winSize, resolution, colsMaxima, maximaMesh); - AdvanceMaximas(map, y+1, min.x, max.x, winSize, resolution, colsMaxima, maximaRows); - } + for (int y = damageMin.y; y <= damageMax.y; ++y) { + FindRadialMaximum(map, y, damageMin.x, damageMax.x, winSize, resolution, colsMaxima, maximaMesh); + AdvanceMaximas(map, y+1, min.x, max.x, winSize, resolution, colsMaxima, maximaRows); + } +} + + +void SmoothHeightMesh::UpdateSmoothMesh() { + SCOPED_TIMER("Sim::SmoothHeightMesh::UpdateSmoothMesh"); + + if (!UpdateSmoothMeshRequired(mapChangeTrack)) return; + + const bool flushBuffer = !mapChangeTrack.activeBuffer; + const bool updateMaxima = !mapChangeTrack.damageQueue[flushBuffer].empty(); + const bool doHorizontalBlur = !mapChangeTrack.horizontalBlurQueue.empty(); + + std::queue* activeQueue = nullptr; + if (updateMaxima) + activeQueue = &mapChangeTrack.damageQueue[flushBuffer]; + else if (doHorizontalBlur) + activeQueue = &mapChangeTrack.horizontalBlurQueue; + else + activeQueue = &mapChangeTrack.verticalBlurQueue; + + const int damagedAreaIndex = activeQueue->front(); + activeQueue->pop(); + + // area of the map which to recalculate the height values + const int damageX = damagedAreaIndex % mapChangeTrack.width; + const int damageY = damagedAreaIndex / mapChangeTrack.width; + int2 damageMin{damageX*SAMPLES_PER_QUAD, damageY*SAMPLES_PER_QUAD}; + int2 damageMax = damageMin + int2{SAMPLES_PER_QUAD - 1, SAMPLES_PER_QUAD - 1}; + + damageMin.x = std::clamp(damageMin.x, 0, maxx - 1); + damageMin.y = std::clamp(damageMin.y, 0, maxy - 1); + damageMax.x = std::clamp(damageMax.x, 0, maxx - 1); + damageMax.y = std::clamp(damageMax.y, 0, maxy - 1); + + if (updateMaxima) { + UpdateSmoothMeshMaximas(damageMin, damageMax); mapChangeTrack.horizontalBlurQueue.push(damagedAreaIndex); mapChangeTrack.damageMap[damagedAreaIndex] = false; } else { + const int winSize = smoothRadius / resolution; + const int blurSize = std::max(1, winSize / 2); + const int2 map{maxx, maxy}; #ifdef SMOOTH_MESH_DEBUG_GENERAL - LOG("%s: quad index %d (%d,%d)-(%d,%d) (%d,%d)-(%d,%d) applying blur", - __func__, - damagedAreaIndex, - damageMin.x, damageMin.y, - damageMax.x, damageMax.y, - min.x, min.y, - max.x, max.y - ); - - LOG("%s: quad area in world space (%f,%f) (%f,%f)", __func__ - , (float)damageMin.x * fresolution - , (float)damageMin.y * fresolution - , (float)damageMax.x * fresolution - , (float)damageMax.y * fresolution - ); + LOG("%s: quad index %d (%d,%d)-(%d,%d) applying blur", __func__ + , damagedAreaIndex, damageMin.x, damageMin.y, damageMax.x, damageMax.y + ); + + LOG("%s: quad area in world space (%f,%f) (%f,%f)", __func__ + , (float)damageMin.x * fresolution, (float)damageMin.y * fresolution + , (float)damageMax.x * fresolution, (float)damageMax.y * fresolution + ); #endif if (doHorizontalBlur) { @@ -585,27 +600,6 @@ void SmoothHeightMesh::UpdateSmoothMesh() { } } -void SmoothHeightMesh::InitMapChangeTracking() { - const int damageTrackWidth = maxx / SAMPLES_PER_QUAD + (maxx % SAMPLES_PER_QUAD ? 1 : 0); - const int damageTrackHeight = maxy / SAMPLES_PER_QUAD + (maxy % SAMPLES_PER_QUAD ? 1 : 0); - const int damageTrackQuads = damageTrackWidth * damageTrackHeight; - mapChangeTrack.damageMap.clear(); - mapChangeTrack.damageMap.resize(damageTrackQuads); - mapChangeTrack.width = damageTrackWidth; - mapChangeTrack.height = damageTrackHeight; -} - -void SmoothHeightMesh::InitDataStructures() { - assert(mesh.empty()); - maximaMesh.resize(maxx * maxy, 0.0f); - mesh.resize(maxx * maxy, 0.0f); - tempMesh.resize(maxx * maxy, 0.0f); - origMesh.resize(maxx * maxy, 0.0f); - colsMaxima.clear(); - colsMaxima.resize(maxx, -std::numeric_limits::max()); - maximaRows.clear(); - maximaRows.resize(maxx, -1); -} void SmoothHeightMesh::MakeSmoothMesh() { ScopedOnceTimer timer("SmoothHeightMesh::MakeSmoothMesh"); diff --git a/rts/Sim/Misc/SmoothHeightMesh.h b/rts/Sim/Misc/SmoothHeightMesh.h index 322091d8a4..53f9ffcd7b 100644 --- a/rts/Sim/Misc/SmoothHeightMesh.h +++ b/rts/Sim/Misc/SmoothHeightMesh.h @@ -63,7 +63,7 @@ class SmoothHeightMesh void InitMapChangeTracking(); void InitDataStructures(); void MakeSmoothMesh(); - + void UpdateSmoothMeshMaximas(int2 damageMin, int2 damageMax); int maxx = 0; int maxy = 0; From 8b65caf2d0c56eac251565e4bd660f8b2bc532d9 Mon Sep 17 00:00:00 2001 From: Marcus Hutchings Date: Tue, 31 May 2022 17:21:04 +0100 Subject: [PATCH 14/20] adjusted assertion expectations. --- rts/Sim/Misc/SmoothHeightMesh.cpp | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/rts/Sim/Misc/SmoothHeightMesh.cpp b/rts/Sim/Misc/SmoothHeightMesh.cpp index 47d25cdfaa..eabc0d2b89 100644 --- a/rts/Sim/Misc/SmoothHeightMesh.cpp +++ b/rts/Sim/Misc/SmoothHeightMesh.cpp @@ -281,10 +281,8 @@ inline static void AdvanceMaximas( LOG("%s: y:%d x:%d column max: %f", __func__, y, x, colsMaxima[x]); #endif -#ifndef NDEBUG assert(maximaRows[x] <= maxy); - assert(maximaRows[x] >= (y - winSize) + 1); -#endif + assert(maximaRows[x] >= y - winSize); } } @@ -326,19 +324,17 @@ inline static void BlurHorizontal( const float gh = GetRealGroundHeight(x, y, resolution); smoothed[x + y * lineSize] = std::max(gh, avg*weight); -#ifndef NDEBUG - assert(smoothed[x + y * lineSize] <= readMap->GetCurrMaxHeight() ); - assert(smoothed[x + y * lineSize] >= readMap->GetCurrMinHeight() ); -#endif - // Get the values to add/remove for next iteration lv = mesh[std::max(0, std::min(li, mapMaxX)) + y * lineSize]; rv = mesh[ std::min(ri, mapMaxX) + y * lineSize]; li++; ri++; #ifdef SMOOTH_MESH_DEBUG_BLUR - LOG("%s: x: %d, y: %d, avg: %f (%f) (g: %f)", __func__, x, y, avg, avg*weight, gh); + LOG ( "%s: x: %d, y: %d, avg: %f (%f) (g: %f) (max h: %f)" + , __func__, x, y, avg, avg*weight, gh, readMap->GetCurrMaxHeight()); #endif + assert(smoothed[x + y * lineSize] <= readMap->GetCurrMaxHeight()+1 ); + assert(smoothed[x + y * lineSize] >= readMap->GetCurrMinHeight()-1 ); } } } @@ -380,11 +376,6 @@ inline static void BlurVertical( const float gh = GetRealGroundHeight(x, y, resolution); smoothed[x + y * lineSize] = std::max(gh, avg*weight); -#ifndef NDEBUG - assert(smoothed[x + y * lineSize] <= readMap->GetCurrMaxHeight() ); - assert(smoothed[x + y * lineSize] >= readMap->GetCurrMinHeight() ); -#endif - lv = mesh[ x + std::max(0, std::min(li, mapMaxY)) * lineSize]; rv = mesh[ x + std::min(ri, mapMaxY) * lineSize]; li++; ri++; @@ -393,6 +384,8 @@ inline static void BlurVertical( LOG("%s: x: %d, y: %d, avg: %f (%f) (g: %f)", __func__, x, y, avg, avg*weight, gh); LOG("%s: for next line -%f +%f", __func__, lv, rv); #endif + assert(smoothed[x + y * lineSize] <= readMap->GetCurrMaxHeight()+1 ); + assert(smoothed[x + y * lineSize] >= readMap->GetCurrMinHeight()-1 ); } } } From 9be30f745bb66c28534b6fa45b4803db8b59fbf7 Mon Sep 17 00:00:00 2001 From: Marcus Hutchings Date: Tue, 31 May 2022 17:53:54 +0100 Subject: [PATCH 15/20] added mod option to enable/disable smooth mesh. --- rts/Sim/Misc/ModInfo.cpp | 4 ++++ rts/Sim/Misc/ModInfo.h | 2 ++ rts/Sim/Misc/SmoothHeightMesh.cpp | 12 ++++++++++-- rts/Sim/Misc/SmoothHeightMesh.h | 5 ++--- 4 files changed, 18 insertions(+), 5 deletions(-) diff --git a/rts/Sim/Misc/ModInfo.cpp b/rts/Sim/Misc/ModInfo.cpp index 4b2bad32a7..bcef3a7aed 100644 --- a/rts/Sim/Misc/ModInfo.cpp +++ b/rts/Sim/Misc/ModInfo.cpp @@ -98,6 +98,8 @@ void CModInfo::ResetState() pfRawDistMult = 1.25f; pfUpdateRate = 0.007f; + enableSmoothMesh = true; + allowTake = true; } } @@ -137,6 +139,8 @@ void CModInfo::Init(const std::string& modFileName) pfRawDistMult = system.GetFloat("pathFinderRawDistMult", pfRawDistMult); pfUpdateRate = system.GetFloat("pathFinderUpdateRate", pfUpdateRate); + enableSmoothMesh = system.GetBool("enableSmoothMesh", enableSmoothMesh); + allowTake = system.GetBool("allowTake", allowTake); } diff --git a/rts/Sim/Misc/ModInfo.h b/rts/Sim/Misc/ModInfo.h index 02389d71ee..dc23c0a396 100644 --- a/rts/Sim/Misc/ModInfo.h +++ b/rts/Sim/Misc/ModInfo.h @@ -168,6 +168,8 @@ class CModInfo float pfRawDistMult; float pfUpdateRate; + bool enableSmoothMesh; + bool allowTake; }; diff --git a/rts/Sim/Misc/SmoothHeightMesh.cpp b/rts/Sim/Misc/SmoothHeightMesh.cpp index eabc0d2b89..5b5e5f3153 100644 --- a/rts/Sim/Misc/SmoothHeightMesh.cpp +++ b/rts/Sim/Misc/SmoothHeightMesh.cpp @@ -8,12 +8,13 @@ #include "Map/Ground.h" #include "Map/ReadMap.h" +#include "Sim/Misc/ModInfo.h" #include "System/float3.h" +#include "System/Log/ILog.h" #include "System/SpringMath.h" #include "System/TimeProfiler.h" #include "System/Threading/ThreadPool.h" -#include "System/Log/ILog.h" using namespace SmoothHeightMeshNamespace; @@ -58,6 +59,8 @@ void SmoothHeightMesh::Init(int2 max, int res, int smoothRad) { Kill(); + enabled = modInfo.enableSmoothMesh; + // we use SSE in performance sensitive code, don't let the window size be too small. if (smoothRad < 4) smoothRad = 4; @@ -73,7 +76,8 @@ void SmoothHeightMesh::Init(int2 max, int res, int smoothRad) InitMapChangeTracking(); InitDataStructures(); - MakeSmoothMesh(); + + if (enabled) MakeSmoothMesh(); } void SmoothHeightMesh::InitMapChangeTracking() { @@ -419,6 +423,8 @@ inline static void CheckInvariants( void SmoothHeightMesh::MapChanged(int x1, int y1, int x2, int y2) { + if (!enabled) return; + const bool queueWasEmpty = mapChangeTrack.damageQueue[mapChangeTrack.activeBuffer].empty(); const int res = resolution*SAMPLES_PER_QUAD; const int w = mapChangeTrack.width; @@ -531,6 +537,8 @@ LOG("%s: quad area in world space (%f,%f) (%f,%f)", __func__ void SmoothHeightMesh::UpdateSmoothMesh() { + if (!enabled) return; + SCOPED_TIMER("Sim::SmoothHeightMesh::UpdateSmoothMesh"); if (!UpdateSmoothMeshRequired(mapChangeTrack)) return; diff --git a/rts/Sim/Misc/SmoothHeightMesh.h b/rts/Sim/Misc/SmoothHeightMesh.h index 53f9ffcd7b..1ebca6540d 100644 --- a/rts/Sim/Misc/SmoothHeightMesh.h +++ b/rts/Sim/Misc/SmoothHeightMesh.h @@ -65,6 +65,8 @@ class SmoothHeightMesh void MakeSmoothMesh(); void UpdateSmoothMeshMaximas(int2 damageMin, int2 damageMax); + bool enabled = true; + int maxx = 0; int maxy = 0; float fmaxx = 0.0f; @@ -82,9 +84,6 @@ class SmoothHeightMesh std::vector maximaRows; MapChangeTrack mapChangeTrack; - - void UpdateMapMaximaGrid(); - void BuildNewMapMaximaGrid(); }; extern SmoothHeightMesh smoothGround; From 2b6fcabe67e895a3420f19fe17ed60eca0965269 Mon Sep 17 00:00:00 2001 From: Marcus Hutchings Date: Tue, 31 May 2022 18:18:00 +0100 Subject: [PATCH 16/20] cleaned by mess in Game.cpp --- rts/Game/Game.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/rts/Game/Game.cpp b/rts/Game/Game.cpp index 0ce0c751f8..04151ac6c6 100644 --- a/rts/Game/Game.cpp +++ b/rts/Game/Game.cpp @@ -574,9 +574,7 @@ void CGame::PreLoadSimulation(LuaParser* defsParser) ENTER_SYNCED_CODE(); loadscreen->SetLoadMessage("Creating Smooth Height Mesh"); - smoothGround.Init (/*float3::maxxpos, float3::maxzpos*/int2(mapDims.mapx, mapDims.mapy) - , /*SQUARE_SIZE **/ 2 - , /*SQUARE_SIZE **/ 40); + smoothGround.Init(int2(mapDims.mapx, mapDims.mapy), 2, 40); loadscreen->SetLoadMessage("Creating QuadField & CEGs"); moveDefHandler.Init(defsParser); From 12e6465f3d47c05bdf5c6b49bb8f7ef5331dc159 Mon Sep 17 00:00:00 2001 From: Marcus Hutchings Date: Wed, 1 Jun 2022 12:13:43 +0100 Subject: [PATCH 17/20] fixed issue with Lua commands that modify the smooth mesh. --- rts/Lua/LuaSyncedCtrl.cpp | 15 +++++++++------ rts/Sim/Misc/SmoothHeightMesh.cpp | 3 +++ 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/rts/Lua/LuaSyncedCtrl.cpp b/rts/Lua/LuaSyncedCtrl.cpp index 406532214d..ca9279ad4d 100644 --- a/rts/Lua/LuaSyncedCtrl.cpp +++ b/rts/Lua/LuaSyncedCtrl.cpp @@ -4214,8 +4214,10 @@ static inline void ParseSmoothMeshParams(lua_State* L, const char* caller, float& factor, int& x1, int& z1, int& x2, int& z2) { ParseParams(L, caller, factor, x1, z1, x2, z2, - smoothGround.GetResolution(), smoothGround.GetMaxX(), - smoothGround.GetMaxY()); + smoothGround.GetResolution(), + smoothGround.GetMaxX() - 1, + smoothGround.GetMaxY() - 1); + } @@ -4231,6 +4233,7 @@ int LuaSyncedCtrl::LevelSmoothMesh(lua_State* L) smoothGround.SetHeight(index, height); } } + return 0; } @@ -4298,8 +4301,8 @@ int LuaSyncedCtrl::AddSmoothMesh(lua_State* L) const int z = (int)(zl / smoothGround.GetResolution()); // discard invalid coordinates - if ((x < 0) || (x > smoothGround.GetMaxX()) || - (z < 0) || (z > smoothGround.GetMaxY())) { + if ((x < 0) || (x > smoothGround.GetMaxX() - 1) || + (z < 0) || (z > smoothGround.GetMaxY() - 1)) { return 0; } @@ -4328,8 +4331,8 @@ int LuaSyncedCtrl::SetSmoothMesh(lua_State* L) const int z = (int)(zl / smoothGround.GetResolution()); // discard invalid coordinates - if ((x < 0) || (x > smoothGround.GetMaxX()) || - (z < 0) || (z > smoothGround.GetMaxY())) { + if ((x < 0) || (x > smoothGround.GetMaxX() - 1) || + (z < 0) || (z > smoothGround.GetMaxY() - 1)) { return 0; } diff --git a/rts/Sim/Misc/SmoothHeightMesh.cpp b/rts/Sim/Misc/SmoothHeightMesh.cpp index 5b5e5f3153..b13e50afcd 100644 --- a/rts/Sim/Misc/SmoothHeightMesh.cpp +++ b/rts/Sim/Misc/SmoothHeightMesh.cpp @@ -128,16 +128,19 @@ float SmoothHeightMesh::GetHeightAboveWater(float x, float y) float SmoothHeightMesh::SetHeight(int index, float h) { + assert(index < maxx*maxy); return (mesh[index] = h); } float SmoothHeightMesh::AddHeight(int index, float h) { + assert(index < maxx*maxy); return (mesh[index] += h); } float SmoothHeightMesh::SetMaxHeight(int index, float h) { + assert(index < maxx*maxy); return (mesh[index] = std::max(h, mesh[index])); } From 59dc41a125812f507486eb0632b5e9939186a934 Mon Sep 17 00:00:00 2001 From: Marcus Hutchings Date: Wed, 1 Jun 2022 14:01:10 +0100 Subject: [PATCH 18/20] Adjusted airmesh drawing to avoid overdraw. --- rts/Rendering/SmoothHeightMeshDrawer.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/rts/Rendering/SmoothHeightMeshDrawer.cpp b/rts/Rendering/SmoothHeightMeshDrawer.cpp index 25a2623ab7..ecab6d2a6b 100644 --- a/rts/Rendering/SmoothHeightMeshDrawer.cpp +++ b/rts/Rendering/SmoothHeightMeshDrawer.cpp @@ -96,10 +96,10 @@ void SmoothHeightMeshDrawer::Draw(float yoffset) { CVertexArray* va = GetVertexArray(); va->Initialize(); - va->EnlargeArrays((numQuadsX + 1) * (numQuadsZ + 1) * 4, 0, VA_SIZE_0); + va->EnlargeArrays(numQuadsX * numQuadsZ * 4, 0, VA_SIZE_0); - for (unsigned int zq = 0; zq <= numQuadsZ; zq++) { - for (unsigned int xq = 0; xq <= numQuadsX; xq++) { + for (unsigned int zq = 0; zq < numQuadsZ; zq++) { + for (unsigned int xq = 0; xq < numQuadsX; xq++) { const float x = xq * quadSize; const float z = zq * quadSize; From a1f12dff03dc5b9018fc7de07107b9c29af03a6f Mon Sep 17 00:00:00 2001 From: Marcus Hutchings Date: Thu, 2 Jun 2022 09:00:27 +0100 Subject: [PATCH 19/20] Ensured SmoothHeightMeshDrawer gets cleared between matches. --- rts/Rendering/WorldDrawer.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/rts/Rendering/WorldDrawer.cpp b/rts/Rendering/WorldDrawer.cpp index 7707958549..491093d156 100644 --- a/rts/Rendering/WorldDrawer.cpp +++ b/rts/Rendering/WorldDrawer.cpp @@ -156,6 +156,7 @@ void CWorldDrawer::Kill() IGroundDecalDrawer::FreeInstance(); CShaderHandler::FreeInstance(shaderHandler); LuaObjectDrawer::Kill(); + SmoothHeightMeshDrawer::FreeInstance(); numUpdates = 0; } From 490aed03cf523ebacc69ed2ab1fd855338519667 Mon Sep 17 00:00:00 2001 From: Marcus Hutchings Date: Thu, 2 Jun 2022 14:20:29 +0100 Subject: [PATCH 20/20] use spring safe delete --- rts/Rendering/SmoothHeightMeshDrawer.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rts/Rendering/SmoothHeightMeshDrawer.cpp b/rts/Rendering/SmoothHeightMeshDrawer.cpp index ecab6d2a6b..c75e610c64 100644 --- a/rts/Rendering/SmoothHeightMeshDrawer.cpp +++ b/rts/Rendering/SmoothHeightMeshDrawer.cpp @@ -10,6 +10,7 @@ #include "Sim/Misc/SmoothHeightMesh.h" #include "System/EventHandler.h" #include "System/float3.h" +#include "System/SafeUtil.h" using namespace SmoothHeightMeshNamespace; @@ -24,8 +25,7 @@ SmoothHeightMeshDrawer* SmoothHeightMeshDrawer::GetInstance() { void SmoothHeightMeshDrawer::FreeInstance() { if (smoothMeshDrawer != nullptr) { - delete smoothMeshDrawer; - smoothMeshDrawer = nullptr; + spring::SafeDelete(smoothMeshDrawer); } }