From d8e76815e04e2b1eacb871dbf61b0a2897f8f450 Mon Sep 17 00:00:00 2001 From: DaPorkchop_ Date: Thu, 23 Jan 2025 13:02:12 +0100 Subject: [PATCH 1/4] tilerenderer: significantly optimize rendered tile sorting This improves overall render performance by roughly 10%. This has two main differences: - We make separate calls to std::sort for each comparator type, to avoid dynamic dispatch at runtime and allow the comparator to be inlined - We only sort pointers to the tiles instead of tiles themselves. This means substantially less data has to be moved around, as each step in the sorting algorithm only has to swap two pointers instead of repeatedly move-constructing TileImage instances, and is responsible for most of the performance improvement. --- src/mapcraftercore/renderer/tilerenderer.cpp | 58 +++++++++++--------- src/mapcraftercore/renderer/tilerenderer.h | 3 - 2 files changed, 33 insertions(+), 28 deletions(-) diff --git a/src/mapcraftercore/renderer/tilerenderer.cpp b/src/mapcraftercore/renderer/tilerenderer.cpp index b48f70c39..2bf693f79 100644 --- a/src/mapcraftercore/renderer/tilerenderer.cpp +++ b/src/mapcraftercore/renderer/tilerenderer.cpp @@ -19,7 +19,8 @@ #include "tilerenderer.h" -#include +#include //std::sort() +#include #include "blockimages.h" #include "rendermode.h" @@ -65,48 +66,49 @@ void TileRenderer::setShadowEdges(std::array shadow_edges) { this->shadow_edges = shadow_edges; } -TileRenderer::cmpBlockPos* TileRenderer::getTileComparator() const { - switch ((RenderRotation::Direction)render_view->getRotation()){ +template +static void sortTiles(IT begin, IT end, RenderRotation::Direction dir) { + switch (dir){ default: case RenderRotation::TOP_LEFT: - return [](const TileImage& a, const TileImage& b) -> bool { + std::sort(begin, end, [](const TileImage* a, const TileImage* b) -> bool { return - (a.pos.y != b.pos.y) ? (a.pos.y < b.pos.y) : ( - (a.pos.z != b.pos.z) ? (a.pos.z < b.pos.z) : ( - (a.pos.x != b.pos.x) ? (a.pos.x > b.pos.x) : ( + (a->pos.y != b->pos.y) ? (a->pos.y < b->pos.y) : ( + (a->pos.z != b->pos.z) ? (a->pos.z < b->pos.z) : ( + (a->pos.x != b->pos.x) ? (a->pos.x > b->pos.x) : ( false ))); - }; + }); break; case RenderRotation::TOP_RIGHT: - return [](const TileImage& a, const TileImage& b) -> bool { + std::sort(begin, end, [](const TileImage* a, const TileImage* b) -> bool { return - (a.pos.y != b.pos.y) ? (a.pos.y < b.pos.y) : ( - (a.pos.x != b.pos.x) ? (a.pos.x < b.pos.x) : ( - (a.pos.z != b.pos.z) ? (a.pos.z < b.pos.z) : ( + (a->pos.y != b->pos.y) ? (a->pos.y < b->pos.y) : ( + (a->pos.x != b->pos.x) ? (a->pos.x < b->pos.x) : ( + (a->pos.z != b->pos.z) ? (a->pos.z < b->pos.z) : ( false ))); - }; + }); break; case RenderRotation::BOTTOM_RIGHT: - return [](const TileImage& a, const TileImage& b) -> bool { + std::sort(begin, end, [](const TileImage* a, const TileImage* b) -> bool { return - (a.pos.y != b.pos.y) ? (a.pos.y < b.pos.y) : ( - (a.pos.z != b.pos.z) ? (a.pos.z > b.pos.z) : ( - (a.pos.x != b.pos.x) ? (a.pos.x < b.pos.x) : ( + (a->pos.y != b->pos.y) ? (a->pos.y < b->pos.y) : ( + (a->pos.z != b->pos.z) ? (a->pos.z > b->pos.z) : ( + (a->pos.x != b->pos.x) ? (a->pos.x < b->pos.x) : ( false ))); - }; + }); break; case RenderRotation::BOTTOM_LEFT: - return [](const TileImage& a, const TileImage& b) -> bool { + std::sort(begin, end, [](const TileImage* a, const TileImage* b) -> bool { return - (a.pos.y != b.pos.y) ? (a.pos.y < b.pos.y) : ( - (a.pos.x != b.pos.x) ? (a.pos.x > b.pos.x) : ( - (a.pos.z != b.pos.z) ? (a.pos.z > b.pos.z) : ( + (a->pos.y != b->pos.y) ? (a->pos.y < b->pos.y) : ( + (a->pos.x != b->pos.x) ? (a->pos.x > b->pos.x) : ( + (a->pos.z != b->pos.z) ? (a->pos.z > b->pos.z) : ( false ))); - }; + }); break; } } @@ -117,10 +119,16 @@ void TileRenderer::renderTile(const TilePos& tile_pos, RGBAImage& tile) { boost::container::vector tile_images; renderTopBlocks(tile_pos, tile_images); + size_t count = tile_images.size(); + std::vector tile_image_pointers(count); + for (size_t i = 0; i < count; i++) { + tile_image_pointers[i] = &tile_images[i]; + } + // Sort them in order depending of the rotation - boost::range::sort(tile_images, getTileComparator()); + sortTiles(tile_image_pointers.begin(), tile_image_pointers.end(), (RenderRotation::Direction)render_view->getRotation()); - for (auto it = tile_images.begin(); it != tile_images.end(); ++it) { + for (auto it : tile_image_pointers) { tile.alphaBlit(it->image, it->x, it->y); } } diff --git a/src/mapcraftercore/renderer/tilerenderer.h b/src/mapcraftercore/renderer/tilerenderer.h index 57e8b0404..e3b3b5aa5 100644 --- a/src/mapcraftercore/renderer/tilerenderer.h +++ b/src/mapcraftercore/renderer/tilerenderer.h @@ -75,9 +75,6 @@ class TileRenderer { virtual int getTileHeight() const; protected: - // void sortTiles(boost::container::vector& tile_images) const; - typedef bool cmpBlockPos(const TileImage &, const TileImage &); - cmpBlockPos* getTileComparator() const; void renderBlocks(int x, int y, mc::BlockPos top, const mc::BlockDir& dir, boost::container::vector& tile_images); virtual void renderTopBlocks(const TilePos& tile_pos, boost::container::vector& tile_images) {} From d34419cd2e3d1dc33a2ac555afcb77d28d16aa5c Mon Sep 17 00:00:00 2001 From: DaPorkchop_ Date: Mon, 17 Feb 2025 16:18:35 +0100 Subject: [PATCH 2/4] tilerenderer: automatically adjust the initial capacity for the tile_images vector this reduces how often the vector's storage needs to be grown during rendering, which is a pretty expensive process --- src/mapcraftercore/renderer/tilerenderer.cpp | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/mapcraftercore/renderer/tilerenderer.cpp b/src/mapcraftercore/renderer/tilerenderer.cpp index 2bf693f79..f230e1650 100644 --- a/src/mapcraftercore/renderer/tilerenderer.cpp +++ b/src/mapcraftercore/renderer/tilerenderer.cpp @@ -20,6 +20,7 @@ #include "tilerenderer.h" #include //std::sort() +#include #include #include "blockimages.h" @@ -114,12 +115,22 @@ static void sortTiles(IT begin, IT end, RenderRotation::Direction dir) { } void TileRenderer::renderTile(const TilePos& tile_pos, RGBAImage& tile) { + static std::atomic tile_images_maxsize(64 << 10); //64Ki + tile.setSize(getTileWidth(), getTileHeight()); + size_t tile_images_capacity_value = tile_images_maxsize; + boost::container::vector tile_images; + tile_images.reserve(tile_images_capacity_value * 2); renderTopBlocks(tile_pos, tile_images); - size_t count = tile_images.size(); + size_t count = tile_images.size(); + if (count > tile_images_capacity_value + && tile_images_maxsize.compare_exchange_strong(tile_images_capacity_value, count)) { + LOG(DEBUG) << "raised initial tile_images capacity to " << count; + } + std::vector tile_image_pointers(count); for (size_t i = 0; i < count; i++) { tile_image_pointers[i] = &tile_images[i]; From c605527b7311d67e7bea5c2d1669725b3801c144 Mon Sep 17 00:00:00 2001 From: DaPorkchop_ Date: Sun, 23 Feb 2025 17:39:12 +0100 Subject: [PATCH 3/4] use std::vector for tilerenderer --- .../renderer/renderviews/isometricnew/tilerenderer.cpp | 2 +- .../renderer/renderviews/isometricnew/tilerenderer.h | 2 +- .../renderer/renderviews/side/tilerenderer.cpp | 2 +- src/mapcraftercore/renderer/renderviews/side/tilerenderer.h | 2 +- .../renderer/renderviews/topdown/tilerenderer.cpp | 2 +- .../renderer/renderviews/topdown/tilerenderer.h | 2 +- src/mapcraftercore/renderer/tilerenderer.cpp | 4 ++-- src/mapcraftercore/renderer/tilerenderer.h | 5 ++--- 8 files changed, 10 insertions(+), 11 deletions(-) diff --git a/src/mapcraftercore/renderer/renderviews/isometricnew/tilerenderer.cpp b/src/mapcraftercore/renderer/renderviews/isometricnew/tilerenderer.cpp index 8356f3c5e..99e7a161c 100644 --- a/src/mapcraftercore/renderer/renderviews/isometricnew/tilerenderer.cpp +++ b/src/mapcraftercore/renderer/renderviews/isometricnew/tilerenderer.cpp @@ -183,7 +183,7 @@ int NewIsometricTileRenderer::getTileSize() const { return images->getBlockSize() * 16 * tile_width; } -void NewIsometricTileRenderer::renderTopBlocks(const TilePos& tile_pos, boost::container::vector& tile_images) { +void NewIsometricTileRenderer::renderTopBlocks(const TilePos& tile_pos, std::vector& tile_images) { int block_size = images->getBlockSize(); mc::BlockDir dir = render_view->getRotation().rotate(mc::DIR_NORTH + mc::DIR_EAST + mc::DIR_BOTTOM); for (old::TileTopBlockIterator it(tile_pos, block_size, tile_width, render_view); !it.end(); it.next()) { diff --git a/src/mapcraftercore/renderer/renderviews/isometricnew/tilerenderer.h b/src/mapcraftercore/renderer/renderviews/isometricnew/tilerenderer.h index b660bc969..7dfccc1a7 100644 --- a/src/mapcraftercore/renderer/renderviews/isometricnew/tilerenderer.h +++ b/src/mapcraftercore/renderer/renderviews/isometricnew/tilerenderer.h @@ -90,7 +90,7 @@ class NewIsometricTileRenderer : public TileRenderer { virtual int getTileSize() const; protected: - virtual void renderTopBlocks(const TilePos& tile_pos, boost::container::vector& tile_images); + virtual void renderTopBlocks(const TilePos& tile_pos, std::vector& tile_images); }; } diff --git a/src/mapcraftercore/renderer/renderviews/side/tilerenderer.cpp b/src/mapcraftercore/renderer/renderviews/side/tilerenderer.cpp index 02489146a..789303839 100644 --- a/src/mapcraftercore/renderer/renderviews/side/tilerenderer.cpp +++ b/src/mapcraftercore/renderer/renderviews/side/tilerenderer.cpp @@ -63,7 +63,7 @@ int SideTileRenderer::getTileHeight() const { return block_images->getBlockHeight() * 8 * tile_width; } -void SideTileRenderer::renderTopBlocks(const TilePos& tile_pos, boost::container::vector& tile_images) { +void SideTileRenderer::renderTopBlocks(const TilePos& tile_pos, std::vector& tile_images) { int block_width = block_images->getBlockWidth(); int block_height = block_images->getBlockHeight(); for (int cx = 0; cx < tile_width; cx++) { diff --git a/src/mapcraftercore/renderer/renderviews/side/tilerenderer.h b/src/mapcraftercore/renderer/renderviews/side/tilerenderer.h index 2fba3f921..dcabf70a1 100644 --- a/src/mapcraftercore/renderer/renderviews/side/tilerenderer.h +++ b/src/mapcraftercore/renderer/renderviews/side/tilerenderer.h @@ -40,7 +40,7 @@ class SideTileRenderer : public TileRenderer { virtual int getTileHeight() const; protected: - virtual void renderTopBlocks(const TilePos& tile_pos, boost::container::vector& tile_images); + virtual void renderTopBlocks(const TilePos& tile_pos, std::vector& tile_images); }; } diff --git a/src/mapcraftercore/renderer/renderviews/topdown/tilerenderer.cpp b/src/mapcraftercore/renderer/renderviews/topdown/tilerenderer.cpp index 778e2a9d2..2abcb463a 100644 --- a/src/mapcraftercore/renderer/renderviews/topdown/tilerenderer.cpp +++ b/src/mapcraftercore/renderer/renderviews/topdown/tilerenderer.cpp @@ -53,7 +53,7 @@ int TopdownTileRenderer::getTileSize() const { return images->getBlockSize() * 16 * tile_width; } -void TopdownTileRenderer::renderTopBlocks(const TilePos& tile_pos, boost::container::vector& tile_images) { +void TopdownTileRenderer::renderTopBlocks(const TilePos& tile_pos, std::vector& tile_images) { int block_size = images->getBlockSize(); for (int cx = 0; cx < tile_width; cx++) { for (int cz = 0; cz < tile_width; cz++) { diff --git a/src/mapcraftercore/renderer/renderviews/topdown/tilerenderer.h b/src/mapcraftercore/renderer/renderviews/topdown/tilerenderer.h index ee0491e75..d8c74a2bf 100644 --- a/src/mapcraftercore/renderer/renderviews/topdown/tilerenderer.h +++ b/src/mapcraftercore/renderer/renderviews/topdown/tilerenderer.h @@ -38,7 +38,7 @@ class TopdownTileRenderer : public TileRenderer { virtual int getTileSize() const; protected: - virtual void renderTopBlocks(const TilePos& tile_pos, boost::container::vector& tile_images); + virtual void renderTopBlocks(const TilePos& tile_pos, std::vector& tile_images); }; } diff --git a/src/mapcraftercore/renderer/tilerenderer.cpp b/src/mapcraftercore/renderer/tilerenderer.cpp index f230e1650..e722ee92e 100644 --- a/src/mapcraftercore/renderer/tilerenderer.cpp +++ b/src/mapcraftercore/renderer/tilerenderer.cpp @@ -121,7 +121,7 @@ void TileRenderer::renderTile(const TilePos& tile_pos, RGBAImage& tile) { size_t tile_images_capacity_value = tile_images_maxsize; - boost::container::vector tile_images; + std::vector tile_images; tile_images.reserve(tile_images_capacity_value * 2); renderTopBlocks(tile_pos, tile_images); @@ -152,7 +152,7 @@ int TileRenderer::getTileHeight() const { return getTileSize(); } -void TileRenderer::renderBlocks(int x, int y, mc::BlockPos top, const mc::BlockDir& dir, boost::container::vector& tile_images) { +void TileRenderer::renderBlocks(int x, int y, mc::BlockPos top, const mc::BlockDir& dir, std::vector& tile_images) { for (; top.y >= mc::CHUNK_LOWEST*16 ; top += dir) { // get current chunk position diff --git a/src/mapcraftercore/renderer/tilerenderer.h b/src/mapcraftercore/renderer/tilerenderer.h index e3b3b5aa5..f2de3cced 100644 --- a/src/mapcraftercore/renderer/tilerenderer.h +++ b/src/mapcraftercore/renderer/tilerenderer.h @@ -27,7 +27,6 @@ #include #include #include -#include namespace fs = boost::filesystem; @@ -75,8 +74,8 @@ class TileRenderer { virtual int getTileHeight() const; protected: - void renderBlocks(int x, int y, mc::BlockPos top, const mc::BlockDir& dir, boost::container::vector& tile_images); - virtual void renderTopBlocks(const TilePos& tile_pos, boost::container::vector& tile_images) {} + void renderBlocks(int x, int y, mc::BlockPos top, const mc::BlockDir& dir, std::vector& tile_images); + virtual void renderTopBlocks(const TilePos& tile_pos, std::vector& tile_images) {} mc::Block getBlock(const mc::BlockPos& pos, int get = mc::GET_ID); uint32_t getBiomeColor(const mc::BlockPos& pos, const BlockImage& block, const mc::Chunk* chunk); From b09a0c4dbfd401274d4a39b16e59648a301d8bfb Mon Sep 17 00:00:00 2001 From: DaPorkchop_ Date: Thu, 3 Jul 2025 15:29:40 +0200 Subject: [PATCH 4/4] tilerenderer: don't initialize tile_image_pointers vector this saves an additional memset --- src/mapcraftercore/renderer/tilerenderer.cpp | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/mapcraftercore/renderer/tilerenderer.cpp b/src/mapcraftercore/renderer/tilerenderer.cpp index e722ee92e..ff176d045 100644 --- a/src/mapcraftercore/renderer/tilerenderer.cpp +++ b/src/mapcraftercore/renderer/tilerenderer.cpp @@ -21,6 +21,7 @@ #include //std::sort() #include +#include // std::allocator #include #include "blockimages.h" @@ -32,6 +33,8 @@ #include "../mc/pos.h" #include "../util.h" +#include + namespace mapcrafter { namespace renderer { @@ -131,10 +134,13 @@ void TileRenderer::renderTile(const TilePos& tile_pos, RGBAImage& tile) { LOG(DEBUG) << "raised initial tile_images capacity to " << count; } - std::vector tile_image_pointers(count); - for (size_t i = 0; i < count; i++) { - tile_image_pointers[i] = &tile_images[i]; - } + //get pointers to all the TileImages + std::vector>> tile_image_pointers(count); + std::transform( + tile_images.begin(), tile_images.end(), tile_image_pointers.begin(), + [](TileImage& tile_image) -> TileImage* { + return &tile_image; + }); // Sort them in order depending of the rotation sortTiles(tile_image_pointers.begin(), tile_image_pointers.end(), (RenderRotation::Direction)render_view->getRotation());