diff --git a/src/benchmark/CMakeLists.txt b/src/benchmark/CMakeLists.txt index 5feba345b..787bbcee4 100644 --- a/src/benchmark/CMakeLists.txt +++ b/src/benchmark/CMakeLists.txt @@ -1,5 +1,6 @@ set (BENCHMARK_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/benchmark.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/benchmark_lighting.cpp ${CMAKE_CURRENT_SOURCE_DIR}/benchmark_serialize.cpp PARENT_SCOPE) diff --git a/src/benchmark/benchmark_lighting.cpp b/src/benchmark/benchmark_lighting.cpp new file mode 100644 index 000000000..498553715 --- /dev/null +++ b/src/benchmark/benchmark_lighting.cpp @@ -0,0 +1,86 @@ +/* +Minetest +Copyright (C) 2022 Minetest Authors + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "benchmark_setup.h" +#include "voxelalgorithms.h" +#include "dummygamedef.h" +#include "dummymap.h" + +TEST_CASE("benchmark_lighting") +{ + DummyGameDef gamedef; + NodeDefManager *ndef = gamedef.getWritableNodeDefManager(); + + v3s16 pmin(-16, -16, -16); + v3s16 pmax(15, 15, 15); + v3s16 bpmin = getNodeBlockPos(pmin), bpmax = getNodeBlockPos(pmax); + DummyMap map(&gamedef, bpmin, bpmax); + + content_t content_wall; + { + ContentFeatures f; + f.name = "stone"; + content_wall = ndef->set(f.name, f); + } + + content_t content_light; + { + ContentFeatures f; + f.name = "light"; + f.param_type = CPT_LIGHT; + f.light_propagates = true; + f.light_source = 14; + content_light = ndef->set(f.name, f); + } + + // Make a platform with a light below it. + { + std::map modified_blocks; + MMVManip vm(&map); + vm.initialEmerge(bpmin, bpmax, false); + s32 volume = vm.m_area.getVolume(); + for (s32 i = 0; i < volume; i++) + vm.m_data[i] = MapNode(CONTENT_AIR); + for (s16 z = -10; z <= 10; z++) + for (s16 x = -10; x <= 10; x++) + vm.setNodeNoEmerge(v3s16(x, 1, z), MapNode(content_wall)); + vm.setNodeNoEmerge(v3s16(0, -10, 0), MapNode(content_light)); + voxalgo::blit_back_with_light(&map, &vm, &modified_blocks); + } + + BENCHMARK_ADVANCED("voxalgo::update_lighting_nodes")(Catch::Benchmark::Chronometer meter) { + std::map modified_blocks; + meter.measure([&] { + map.addNodeAndUpdate(v3s16(0, 0, 0), MapNode(content_light), modified_blocks); + map.removeNodeAndUpdate(v3s16(0, 0, 0), modified_blocks); + }); + }; + + BENCHMARK_ADVANCED("voxalgo::blit_back_with_light")(Catch::Benchmark::Chronometer meter) { + std::map modified_blocks; + MMVManip vm(&map); + vm.initialEmerge(v3s16(0, 0, 0), v3s16(0, 0, 0), false); + meter.measure([&] { + vm.setNodeNoEmerge(v3s16(0, 0, 0), MapNode(content_light)); + voxalgo::blit_back_with_light(&map, &vm, &modified_blocks); + vm.setNodeNoEmerge(v3s16(0, 0, 0), MapNode(CONTENT_AIR)); + voxalgo::blit_back_with_light(&map, &vm, &modified_blocks); + }); + }; +} diff --git a/src/dummygamedef.h b/src/dummygamedef.h new file mode 100644 index 000000000..97281ffcd --- /dev/null +++ b/src/dummygamedef.h @@ -0,0 +1,81 @@ +/* +Minetest +Copyright (C) 2013 celeron55, Perttu Ahola +Copyright (C) 2022 TurkeyMcMac, Jude Melton-Houghton + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +#include "gamedef.h" +#include "itemdef.h" +#include "nodedef.h" +#include "craftdef.h" +#include "content/mods.h" +#include "database/database-dummy.h" + +class DummyGameDef : public IGameDef { +public: + DummyGameDef(): + m_itemdef(createItemDefManager()), + m_nodedef(createNodeDefManager()), + m_craftdef(createCraftDefManager()), + m_mod_storage_database(new Database_Dummy()) + { + } + + ~DummyGameDef() + { + delete m_mod_storage_database; + delete m_craftdef; + delete m_nodedef; + delete m_itemdef; + } + + IItemDefManager *getItemDefManager() override { return m_itemdef; } + const NodeDefManager *getNodeDefManager() override { return m_nodedef; } + NodeDefManager* getWritableNodeDefManager() { return m_nodedef; } + ICraftDefManager *getCraftDefManager() override { return m_craftdef; } + + u16 allocateUnknownNodeId(const std::string &name) override + { + return m_nodedef->allocateDummy(name); + } + + const std::vector &getMods() const override + { + static std::vector emptymodspec; + return emptymodspec; + } + const ModSpec* getModSpec(const std::string &modname) const override { return nullptr; } + bool registerModStorage(ModMetadata *meta) override { return true; } + void unregisterModStorage(const std::string &name) override {} + ModMetadataDatabase *getModStorageDatabase() override { return m_mod_storage_database; } + + bool joinModChannel(const std::string &channel) override { return false; } + bool leaveModChannel(const std::string &channel) override { return false; } + bool sendModChannelMessage(const std::string &channel, const std::string &message) override + { + return false; + } + ModChannel *getModChannel(const std::string &channel) override { return nullptr; } + +protected: + IItemDefManager *m_itemdef = nullptr; + NodeDefManager *m_nodedef = nullptr; + ICraftDefManager *m_craftdef = nullptr; + ModMetadataDatabase *m_mod_storage_database = nullptr; +}; diff --git a/src/dummymap.h b/src/dummymap.h new file mode 100644 index 000000000..5f2881371 --- /dev/null +++ b/src/dummymap.h @@ -0,0 +1,43 @@ +/* +Minetest +Copyright (C) 2022 TurkeyMcMac, Jude Melton-Houghton + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +#include "map.h" +#include "mapsector.h" + +class DummyMap : public Map +{ +public: + DummyMap(IGameDef *gamedef, v3s16 bpmin, v3s16 bpmax): Map(gamedef) + { + for (s16 z = bpmin.Z; z <= bpmax.Z; z++) + for (s16 x = bpmin.X; x <= bpmax.X; x++) { + v2s16 p2d(x, z); + MapSector *sector = new MapSector(this, p2d, gamedef); + m_sectors[p2d] = sector; + for (s16 y = bpmin.Y; y <= bpmax.Y; y++) + sector->createBlankBlock(y); + } + } + + ~DummyMap() = default; + + bool maySaveBlocks() override { return false; } +}; diff --git a/src/map.cpp b/src/map.cpp index 49e60cbb5..5a303d9ba 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -183,7 +183,7 @@ static void set_node_in_block(MapBlock *block, v3s16 relpos, MapNode n) } // throws InvalidPositionException if not found -void Map::setNode(v3s16 p, MapNode & n) +void Map::setNode(v3s16 p, MapNode n) { v3s16 blockpos = getNodeBlockPos(p); MapBlock *block = getBlockNoCreate(blockpos); diff --git a/src/map.h b/src/map.h index 6bc2aaaee..27a87e635 100644 --- a/src/map.h +++ b/src/map.h @@ -161,7 +161,7 @@ public: bool isValidPosition(v3s16 p); // throws InvalidPositionException if not found - void setNode(v3s16 p, MapNode & n); + void setNode(v3s16 p, MapNode n); // Returns a CONTENT_IGNORE node if not found // If is_valid_position is not NULL then this will be set to true if the diff --git a/src/unittest/test.cpp b/src/unittest/test.cpp index af30c209d..149ab82b5 100644 --- a/src/unittest/test.cpp +++ b/src/unittest/test.cpp @@ -22,10 +22,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "client/sound.h" #include "nodedef.h" #include "itemdef.h" -#include "gamedef.h" +#include "dummygamedef.h" #include "modchannels.h" -#include "content/mods.h" -#include "database/database-dummy.h" #include "util/numeric.h" #include "porting.h" @@ -42,36 +40,13 @@ content_t t_CONTENT_BRICK; //// TestGameDef //// -class TestGameDef : public IGameDef { +class TestGameDef : public DummyGameDef { public: TestGameDef(); - ~TestGameDef(); - - IItemDefManager *getItemDefManager() { return m_itemdef; } - const NodeDefManager *getNodeDefManager() { return m_nodedef; } - ICraftDefManager *getCraftDefManager() { return m_craftdef; } - ITextureSource *getTextureSource() { return m_texturesrc; } - IShaderSource *getShaderSource() { return m_shadersrc; } - ISoundManager *getSoundManager() { return m_soundmgr; } - scene::ISceneManager *getSceneManager() { return m_scenemgr; } - IRollbackManager *getRollbackManager() { return m_rollbackmgr; } - EmergeManager *getEmergeManager() { return m_emergemgr; } - ModMetadataDatabase *getModStorageDatabase() { return m_mod_storage_database; } - - scene::IAnimatedMesh *getMesh(const std::string &filename) { return NULL; } - bool checkLocalPrivilege(const std::string &priv) { return false; } - u16 allocateUnknownNodeId(const std::string &name) { return 0; } + ~TestGameDef() = default; void defineSomeNodes(); - virtual const std::vector &getMods() const - { - static std::vector testmodspec; - return testmodspec; - } - virtual const ModSpec* getModSpec(const std::string &modname) const { return NULL; } - virtual bool registerModStorage(ModMetadata *meta) { return true; } - virtual void unregisterModStorage(const std::string &name) {} bool joinModChannel(const std::string &channel); bool leaveModChannel(const std::string &channel); bool sendModChannelMessage(const std::string &channel, const std::string &message); @@ -81,39 +56,18 @@ public: } private: - IItemDefManager *m_itemdef = nullptr; - const NodeDefManager *m_nodedef = nullptr; - ICraftDefManager *m_craftdef = nullptr; - ITextureSource *m_texturesrc = nullptr; - IShaderSource *m_shadersrc = nullptr; - ISoundManager *m_soundmgr = nullptr; - scene::ISceneManager *m_scenemgr = nullptr; - IRollbackManager *m_rollbackmgr = nullptr; - EmergeManager *m_emergemgr = nullptr; - ModMetadataDatabase *m_mod_storage_database = nullptr; std::unique_ptr m_modchannel_mgr; }; TestGameDef::TestGameDef() : - m_mod_storage_database(new Database_Dummy()), + DummyGameDef(), m_modchannel_mgr(new ModChannelMgr()) { - m_itemdef = createItemDefManager(); - m_nodedef = createNodeDefManager(); - defineSomeNodes(); } -TestGameDef::~TestGameDef() -{ - delete m_itemdef; - delete m_nodedef; - delete m_mod_storage_database; -} - - void TestGameDef::defineSomeNodes() { IWritableItemDefManager *idef = (IWritableItemDefManager *)m_itemdef; @@ -185,6 +139,8 @@ void TestGameDef::defineSomeNodes() f = ContentFeatures(); f.name = itemdef.name; f.alpha = ALPHAMODE_BLEND; + f.light_propagates = true; + f.param_type = CPT_LIGHT; f.liquid_type = LIQUID_SOURCE; f.liquid_viscosity = 4; f.is_ground_content = true; diff --git a/src/unittest/test_voxelalgorithms.cpp b/src/unittest/test_voxelalgorithms.cpp index 0ffd24b7d..abe48893f 100644 --- a/src/unittest/test_voxelalgorithms.cpp +++ b/src/unittest/test_voxelalgorithms.cpp @@ -22,6 +22,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "gamedef.h" #include "voxelalgorithms.h" #include "util/numeric.h" +#include "dummymap.h" class TestVoxelAlgorithms : public TestBase { public: @@ -30,21 +31,21 @@ public: void runTests(IGameDef *gamedef); - void testVoxelLineIterator(const NodeDefManager *ndef); + void testVoxelLineIterator(); + void testLighting(IGameDef *gamedef); }; static TestVoxelAlgorithms g_test_instance; void TestVoxelAlgorithms::runTests(IGameDef *gamedef) { - const NodeDefManager *ndef = gamedef->getNodeDefManager(); - - TEST(testVoxelLineIterator, ndef); + TEST(testVoxelLineIterator); + TEST(testLighting, gamedef); } //////////////////////////////////////////////////////////////////////////////// -void TestVoxelAlgorithms::testVoxelLineIterator(const NodeDefManager *ndef) +void TestVoxelAlgorithms::testVoxelLineIterator() { // Test some lines // Do not test lines that start or end on the border of @@ -99,3 +100,65 @@ void TestVoxelAlgorithms::testVoxelLineIterator(const NodeDefManager *ndef) UASSERTEQ(int, actual_nodecount, nodecount); } } + +void TestVoxelAlgorithms::testLighting(IGameDef *gamedef) +{ + v3s16 pmin(-32, -32, -32); + v3s16 pmax(31, 31, 31); + v3s16 bpmin = getNodeBlockPos(pmin), bpmax = getNodeBlockPos(pmax); + DummyMap map(gamedef, bpmin, bpmax); + + // Make a 21x21x21 hollow box centered at the origin. + { + std::map modified_blocks; + MMVManip vm(&map); + vm.initialEmerge(bpmin, bpmax, false); + s32 volume = vm.m_area.getVolume(); + for (s32 i = 0; i < volume; i++) + vm.m_data[i] = MapNode(CONTENT_AIR); + for (s16 z = -10; z <= 10; z++) + for (s16 y = -10; y <= 10; y++) + for (s16 x = -10; x <= 10; x++) + vm.setNodeNoEmerge(v3s16(x, y, z), MapNode(t_CONTENT_STONE)); + for (s16 z = -9; z <= 9; z++) + for (s16 y = -9; y <= 9; y++) + for (s16 x = -9; x <= 9; x++) + vm.setNodeNoEmerge(v3s16(x, y, z), MapNode(CONTENT_AIR)); + voxalgo::blit_back_with_light(&map, &vm, &modified_blocks); + } + + // Place two holes on the edges a torch in the center. + { + std::map modified_blocks; + map.addNodeAndUpdate(v3s16(-10, 0, 0), MapNode(CONTENT_AIR), modified_blocks); + map.addNodeAndUpdate(v3s16(9, 10, -9), MapNode(t_CONTENT_WATER), modified_blocks); + map.addNodeAndUpdate(v3s16(0, 0, 0), MapNode(t_CONTENT_TORCH), modified_blocks); + } + + const NodeDefManager *ndef = gamedef->ndef(); + { + MapNode n = map.getNode(v3s16(9, 9, -9)); + UASSERTEQ(int, n.getLight(LIGHTBANK_NIGHT, ndef), 0); + UASSERTEQ(int, n.getLight(LIGHTBANK_DAY, ndef), 13); + } + { + MapNode n = map.getNode(v3s16(0, 1, 0)); + UASSERTEQ(int, n.getLight(LIGHTBANK_NIGHT, ndef), 12); + UASSERTEQ(int, n.getLight(LIGHTBANK_DAY, ndef), 12); + } + { + MapNode n = map.getNode(v3s16(-9, -1, 0)); + UASSERTEQ(int, n.getLight(LIGHTBANK_NIGHT, ndef), 3); + UASSERTEQ(int, n.getLight(LIGHTBANK_DAY, ndef), 12); + } + { + MapNode n = map.getNode(v3s16(-10, 0, 0)); + UASSERTEQ(int, n.getLight(LIGHTBANK_NIGHT, ndef), 3); + UASSERTEQ(int, n.getLight(LIGHTBANK_DAY, ndef), 14); + } + { + MapNode n = map.getNode(v3s16(-11, 0, 0)); + UASSERTEQ(int, n.getLight(LIGHTBANK_NIGHT, ndef), 2); + UASSERTEQ(int, n.getLight(LIGHTBANK_DAY, ndef), 15); + } +} diff --git a/src/voxelalgorithms.cpp b/src/voxelalgorithms.cpp index ffb70aa71..a11952916 100644 --- a/src/voxelalgorithms.cpp +++ b/src/voxelalgorithms.cpp @@ -826,7 +826,7 @@ void fill_with_sunlight(MMVManip *vm, const NodeDefManager *ndef, v2s16 offset, * is sunlight above the block at the given z-x relative * node coordinates. */ -void is_sunlight_above_block(ServerMap *map, mapblock_v3 pos, +void is_sunlight_above_block(Map *map, mapblock_v3 pos, const NodeDefManager *ndef, bool light[MAP_BLOCKSIZE][MAP_BLOCKSIZE]) { mapblock_v3 source_block_pos = pos + v3s16(0, 1, 0); @@ -1044,7 +1044,7 @@ void finish_bulk_light_update(Map *map, mapblock_v3 minblock, } } -void blit_back_with_light(ServerMap *map, MMVManip *vm, +void blit_back_with_light(Map *map, MMVManip *vm, std::map *modified_blocks) { const NodeDefManager *ndef = map->getNodeDefManager(); @@ -1187,7 +1187,7 @@ void fill_with_sunlight(MapBlock *block, const NodeDefManager *ndef, } } -void repair_block_light(ServerMap *map, MapBlock *block, +void repair_block_light(Map *map, MapBlock *block, std::map *modified_blocks) { if (!block || block->isDummy()) diff --git a/src/voxelalgorithms.h b/src/voxelalgorithms.h index bcbd3b586..1fe070152 100644 --- a/src/voxelalgorithms.h +++ b/src/voxelalgorithms.h @@ -24,7 +24,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "util/container.h" class Map; -class ServerMap; class MapBlock; class MMVManip; @@ -68,7 +67,7 @@ void update_block_border_lighting(Map *map, MapBlock *block, * \param modified_blocks output, contains all map blocks that * the function modified */ -void blit_back_with_light(ServerMap *map, MMVManip *vm, +void blit_back_with_light(Map *map, MMVManip *vm, std::map *modified_blocks); /*! @@ -77,7 +76,7 @@ void blit_back_with_light(ServerMap *map, MMVManip *vm, * * \param block the block to update */ -void repair_block_light(ServerMap *map, MapBlock *block, +void repair_block_light(Map *map, MapBlock *block, std::map *modified_blocks); /*!