Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improved lighting #4655

Merged
merged 3 commits into from Oct 27, 2016
Merged

Improved lighting #4655

merged 3 commits into from Oct 27, 2016

Conversation

juhdanad
Copy link
Contributor

This PR rewrites the procedure that is responsible for light
updating when a single node changes.

The PR

  • provides iterative solutions for unlighting and light spreading
  • introduces a new priority queue-like container for the iteration
  • creates per-node MapBlock caching to reduce retrieving MapBlocks from the map
  • calculates with map block positions and in-block relative node coordinates
  • skips light updating if it is not necessary since the node's new light will be the same as its old light was

This commit rewrites the procedure that is responsible for light
updating.

this commit
-provides iterative solutions for unlighting and light spreading
-introduces a new priority queue-like container for the iteration
-creates per-node MapBlock caching to reduce retrieving MapBlocks from
the map
-calculates with map block positions and in-block relative node
coordinates
-skips light updating if it is not necessary since the node's new light
will be the same as its old light was
@juhdanad
Copy link
Contributor Author

I have made some speed tests, too (in debug build, so Minetest ran slower than its maximal speed).
I tested in singleplayer mode, so the measured times are sums of three durations:
final duration=first client light update duration+server light update duration+second client light update duration (when the server confirms the placement of the block, my version always skips lighting there).
The durations are the average of ~30 measurements.

Single cobblestone placed in sunlight: 0,217 ms -> 0,122 ms
Single cobblestone removed in sunlight: 0,305 ms -> 0,129 ms
Sunlight propagating down in a thick, dark pit (30 node deep): 69,6 ms -> 11,3 ms
Sunlight disappearing from the same pit: 89,5 ms -> 11,3 ms
Placing light source (source=14) in a dark room: 71,9 ms -> 9,4 ms
Removing that light source: 36,2 ms -> 9,4 ms
Placing light source next to same light source (no sunlight): 75,7 ms -> 5,1 ms
Removing that light source: 56 ms -> 9,7 ms
Placing light source (12) next to brighter light source (14): 55 ms -> ~0 ms
Removing the same light source: 53,7 ms -> ~0 ms
Placing light source (14) next to dimmer light source (12): 89,7 ms -> 9 ms
Removing the same light source: 69,4 ms -> 15,8 ms
Placing cobblestone diagonally next to light source (no lights change, so running time should be 0, but implementing this would decrease other running times, I think): 17,7 ms -> 3,7 ms
Removing that cobblestone: 7,8 ms -> ~0 ms

@est31 est31 added this to the 0.4.15 milestone Oct 22, 2016
@SmallJoker
Copy link
Member

SmallJoker commented Oct 22, 2016

Same mesecons delayer circuit in both cases. Works fine on Windows using MSVC.
lightening_before_after

@Fixer-007
Copy link
Contributor

Tried ingame, nothing suspicious yet.

@paramat
Copy link
Contributor

paramat commented Oct 22, 2016

Are bulk node updates affected? Such as lua voxelmanip used after mapgen, placement of schematics, and placement of L-system structures? Currently we have problems with lighting bugs caused by these 3 operations.

@Fixer-007
Copy link
Contributor

Fixer-007 commented Oct 23, 2016

@paramat
I've managed to reproduce infamous shadows caused by TNT with this PR too. This PR will probably not affect known shadow bugs... Lighting bugs live on! :trollface:
https://i.imgur.com/6jT4OMr.jpg

Someone needs to try Vanessas trees (maybe me, but tommorrow).

@juhdanad
Copy link
Contributor Author

@SmallJoker, sorry if I disappointed you, in fact I was also a bit disappointed. When I have been writing this commit, I only measured the speed of light updating with a TimeTaker. I did not take two things into account:

  1. the current light updating procedure is also fast, so making it more efficient does not introduce visible changes.
  2. when a server-side mod alters nodes, the first client light update is not present, so the improvement of the running time is less significant.

Also, since lights do not change in every server step, the improvement is hardly noticeable.

But you can see the performance increase in extreme situations: if you create a big hole in the ground (see the image) with a piston, SEnv::step will be noticeably faster (the server does not compute meshes, so the light updating is more significant).

minetest_light

Sorry if I have misinformed you.

@paramat, @Fixer-007 my code only alters single node changes. This is why it does not correct the TNT lighting bug. I would like to rewrite all other lighting to this new style, but this will be in another PR.

@paramat
Copy link
Contributor

paramat commented Oct 23, 2016

Any work on the lighting update for bulk node changes will be very welcome, our lighting bugs are a big problem, possibly MT's most serious bug.
The lighting bug caused by L-system is detailed here #3421

@SmallJoker
Copy link
Member

Would it be possible to light the bottom face of a node a bit more? This also happens in older Minetest versions and looks somehow strange IMO.
1

@juhdanad
Copy link
Contributor Author

@SmallJoker, the lighting of bottom faces is in the mesh generation code, and is not related to light spreading, it is pure graphical. I think they are darker because their normal vector points downwards. This helps to visually separate the directions of the nodes' faces.

This commit modifies the liquid transforming procedure to light and
unlight nodes instead of whole map blocks.
@juhdanad
Copy link
Contributor Author

juhdanad commented Oct 23, 2016

Now liquid transform uses bulk per-node updating instead of map block updating.
Liquids flowing over glass cast shadows.
I have not made any speed tests yet, but I think flowing lava will be faster now.
Fixes #4658

@paramat paramat added the Bugfix 🐛 PRs that fix a bug label Oct 23, 2016
@nerzhul
Copy link
Member

nerzhul commented Oct 24, 2016

nice code quality (haven't review the feature itself, just the code quality), thanks

* The parameters are the same as in ChangingLight's constructor.
* \param light light level of the ChangingLight
*/
inline void push(u8 light, relative_v3 &rel_pos, mapblock_v3 &block_pos,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please make these references const

for (u8 i = 0; i <= LIGHT_SUN; i++) {
const std::vector<ChangingLight> &lights = light_sources.lights[i];
for (std::vector<ChangingLight>::const_iterator it = lights.begin();
it < lights.end(); it++) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Always use ++it for iterators because it++ creates a wasted copy.

Copy link
Member

@sfan5 sfan5 Oct 25, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I doubt compilers are actually that stupid, do you have a source?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've asked that in C++ once, and they only said "maybe its optimized, maybe not".

Either way, we have commit 34b7a14 and I'd prefer if it++ wouldn't start entering the codebase again.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that it makes sense to follow the general code style, the optimization explanation just seemed weird to me.


void update_lighting_nodes(Map *map, INodeDefManager *ndef,
std::vector<std::pair<v3s16, MapNode> > &oldnodes,
std::map<v3s16, MapBlock*> & modified_blocks)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

spaces

* \param modified_blocks output, all modified map blocks are added to this
*/
void spreadLight(Map *map, INodeDefManager *nodemgr, LightBank bank,
LightQueue & light_sources, std::map<v3s16, MapBlock*> & modified_blocks)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

spaces

*/
void unspreadLight(Map *map, INodeDefManager *nodemgr, LightBank bank,
UnlightQueue &from_nodes, ReLightQueue &light_sources,
std::map<v3s16, MapBlock*> & modified_blocks)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

spaces

* 4=Y-
* 5=X-
* 6=no direction
* Two directions ate opposite only if their sum is 5.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

are*

@@ -19,6 +19,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,

#include "voxelalgorithms.h"
#include "nodedef.h"
#include "mapblock.h"
#include "map.h"
#include "util/timetaker.h"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this still needed?

liquid (=water node was added) to transform queue.
*/
Add neighboring liquid nodes and this node to transform queue.
(it's vital for the node itself to get updated last, if it was removed.)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you explain in the comment why it is important for the node to get updated last?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To tell the truth, I don't know. Since it was written in Map::removeNodeAndUpdate, I didn't want to alter it, because I only wanted to modify code related to lighting and not to introduce liquid flow bugs.
I don't know who wrote this comment originally, but I would be very thankful if he/she explained there why is this important.
If you want me, I will try to find it out myself, but I think it is related to liquids, not lights.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah I see, it appeared from the diff that you added it but if it was there before its okay.

relative_v3 rel_position;
//! Position of the node's block.
mapblock_v3 block_position;
//! Reference to the node's block.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Its a pointer not a reference

struct ChangingLight {
//! Relative position of the node in its map block.
relative_v3 rel_position;
//! Position of the node's block.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add to the comment whether its in node coordinates or in block coordinates

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added to mapblock_v3's description.

@Fixer-007
Copy link
Contributor

I played with it in game and did not find nothing critical yet.

@Zeno-
Copy link
Contributor

Zeno- commented Oct 27, 2016

I've looked over the code and for the most part I think it's great. I don't have any huge issues with it and find it easy to read (meaning it should be easy to maintain I guess).

Spent an hour doing profiling with callgrind with and without this PR and there is certainly a performance difference (improvement with the PR).

valgrind reports no memory related issues related to the PR.

Played a singleplayer game for about 30 minutes. Didn't notice anything unusual with the lighting.

So, in summary I think it's nice work 👍

@est31
Copy link
Contributor

est31 commented Oct 27, 2016

👍

@nerzhul
Copy link
Member

nerzhul commented Oct 27, 2016

Okay for me too.

@nerzhul nerzhul merged commit bcb06ae into minetest:master Oct 27, 2016
@paramat
Copy link
Contributor

paramat commented Oct 27, 2016

@juhdanad does this fix #2823 ?
Here's the water lighting bug i mentioned earlier #3004

@Fixer-007
Copy link
Contributor

Fixer-007 commented Oct 27, 2016

#3004 is fixed by this it seems (good job 👍)
On #2823 bug I have question: #2823 (comment)
If someone interested, even with this PR lava casting ghost lighting still appears... https://i.imgur.com/V9DUnuf.png

@juhdanad
Copy link
Contributor Author

I have noticed that this PR causes a compatibility break - before this PR, shadow next to sunlight had light level 13, after this PR it is changed to 14. This causes that players playing with new clients on old servers might see nodes one light level brighter than they are.

@HybridDog
Copy link
Contributor

@juhdanad, I've got a question about the order in which the ChangingLights are traversed.
If I understand this correctly, unspread_light and spread_light traverse the positions with Breadth First Search and often almost all neighbours are added to the queue with the same light value. When there are no remaining positions for a specific light value, the positions for the next lower light value are traversed. These positions can be ordered quite randomly, which means slower memory accesses.
The ChangingLights could be sorted by block and relative position (z-y-x order) before traversing them by using a Priority Queue instead of a std::vector or by directly using Quicksort when switching to the next lower maxlight.
Would sorting them make light spreading a bit faster or is the memory access order negligible?

struct LightQueue {
//! For each light level there is a vector.
std::vector<ChangingLight> lights[LIGHT_SUN + 1];

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

9 participants