summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorx2048 <codeforsmile@gmail.com>2021-09-27 17:46:08 +0200
committerGitHub <noreply@github.com>2021-09-27 17:46:08 +0200
commitf5040707fe7cf9f24274379598527d6298c5818c (patch)
tree54eebe9df1e8eabf58b077069ecd02f52b3c8f83
parentd51d0f3a5a60679436bf7d4e1980f3a82f229848 (diff)
downloadminetest-f5040707fe7cf9f24274379598527d6298c5818c.tar.gz
minetest-f5040707fe7cf9f24274379598527d6298c5818c.tar.bz2
minetest-f5040707fe7cf9f24274379598527d6298c5818c.zip
Order drawlist by distance to the camera when rendering (#11651)
-rw-r--r--src/client/clientmap.cpp99
-rw-r--r--src/client/clientmap.h30
-rw-r--r--src/client/game.cpp4
3 files changed, 104 insertions, 29 deletions
diff --git a/src/client/clientmap.cpp b/src/client/clientmap.cpp
index 77f3b9fe8..7cde085c8 100644
--- a/src/client/clientmap.cpp
+++ b/src/client/clientmap.cpp
@@ -73,7 +73,8 @@ ClientMap::ClientMap(
rendering_engine->get_scene_manager(), id),
m_client(client),
m_rendering_engine(rendering_engine),
- m_control(control)
+ m_control(control),
+ m_drawlist(MapBlockComparer(v3s16(0,0,0)))
{
/*
@@ -164,6 +165,8 @@ void ClientMap::updateDrawList()
{
ScopeProfiler sp(g_profiler, "CM::updateDrawList()", SPT_AVG);
+ m_needs_update_drawlist = false;
+
for (auto &i : m_drawlist) {
MapBlock *block = i.second;
block->refDrop();
@@ -178,6 +181,7 @@ void ClientMap::updateDrawList()
const f32 camera_fov = m_camera_fov * 1.1f;
v3s16 cam_pos_nodes = floatToInt(camera_position, BS);
+
v3s16 p_blocks_min;
v3s16 p_blocks_max;
getBlocksInViewRange(cam_pos_nodes, &p_blocks_min, &p_blocks_max);
@@ -202,6 +206,8 @@ void ClientMap::updateDrawList()
occlusion_culling_enabled = false;
}
+ v3s16 camera_block = getContainerPos(cam_pos_nodes, MAP_BLOCKSIZE);
+ m_drawlist = std::map<v3s16, MapBlock*, MapBlockComparer>(MapBlockComparer(camera_block));
// Uncomment to debug occluded blocks in the wireframe mode
// TODO: Include this as a flag for an extended debugging setting
@@ -321,7 +327,20 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
Draw the selected MapBlocks
*/
- MeshBufListList drawbufs;
+ MeshBufListList grouped_buffers;
+
+ struct DrawDescriptor {
+ v3s16 m_pos;
+ scene::IMeshBuffer *m_buffer;
+ bool m_reuse_material;
+
+ DrawDescriptor(const v3s16 &pos, scene::IMeshBuffer *buffer, bool reuse_material) :
+ m_pos(pos), m_buffer(buffer), m_reuse_material(reuse_material)
+ {}
+ };
+
+ std::vector<DrawDescriptor> draw_order;
+ video::SMaterial previous_material;
for (auto &i : m_drawlist) {
v3s16 block_pos = i.first;
@@ -386,53 +405,76 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
material.setFlag(video::EMF_WIREFRAME,
m_control.show_wireframe);
- drawbufs.add(buf, block_pos, layer);
+ if (is_transparent_pass) {
+ // Same comparison as in MeshBufListList
+ bool new_material = material.getTexture(0) != previous_material.getTexture(0) ||
+ material != previous_material;
+
+ draw_order.emplace_back(block_pos, buf, !new_material);
+
+ if (new_material)
+ previous_material = material;
+ }
+ else {
+ grouped_buffers.add(buf, block_pos, layer);
+ }
}
}
}
}
}
+ // Capture draw order for all solid meshes
+ for (auto &lists : grouped_buffers.lists) {
+ for (MeshBufList &list : lists) {
+ // iterate in reverse to draw closest blocks first
+ for (auto it = list.bufs.rbegin(); it != list.bufs.rend(); ++it) {
+ draw_order.emplace_back(it->first, it->second, it != list.bufs.rbegin());
+ }
+ }
+ }
+
TimeTaker draw("Drawing mesh buffers");
core::matrix4 m; // Model matrix
v3f offset = intToFloat(m_camera_offset, BS);
+ u32 material_swaps = 0;
- // Render all layers in order
- for (auto &lists : drawbufs.lists) {
- for (MeshBufList &list : lists) {
- // Check and abort if the machine is swapping a lot
- if (draw.getTimerTime() > 2000) {
- infostream << "ClientMap::renderMap(): Rendering took >2s, " <<
- "returning." << std::endl;
- return;
- }
+ // Render all mesh buffers in order
+ drawcall_count += draw_order.size();
+ for (auto &descriptor : draw_order) {
+ scene::IMeshBuffer *buf = descriptor.m_buffer;
+ // Check and abort if the machine is swapping a lot
+ if (draw.getTimerTime() > 2000) {
+ infostream << "ClientMap::renderMap(): Rendering took >2s, " <<
+ "returning." << std::endl;
+ return;
+ }
+
+ if (!descriptor.m_reuse_material) {
+ auto &material = buf->getMaterial();
// pass the shadow map texture to the buffer texture
ShadowRenderer *shadow = m_rendering_engine->get_shadow_renderer();
if (shadow && shadow->is_active()) {
- auto &layer = list.m.TextureLayer[3];
+ auto &layer = material.TextureLayer[3];
layer.Texture = shadow->get_texture();
layer.TextureWrapU = video::E_TEXTURE_CLAMP::ETC_CLAMP_TO_EDGE;
layer.TextureWrapV = video::E_TEXTURE_CLAMP::ETC_CLAMP_TO_EDGE;
layer.TrilinearFilter = true;
}
+ driver->setMaterial(material);
+ ++material_swaps;
+ }
- driver->setMaterial(list.m);
-
- drawcall_count += list.bufs.size();
- for (auto &pair : list.bufs) {
- scene::IMeshBuffer *buf = pair.second;
+ v3f block_wpos = intToFloat(descriptor.m_pos * MAP_BLOCKSIZE, BS);
+ m.setTranslation(block_wpos - offset);
- v3f block_wpos = intToFloat(pair.first * MAP_BLOCKSIZE, BS);
- m.setTranslation(block_wpos - offset);
-
- driver->setTransform(video::ETS_WORLD, m);
- driver->drawMeshBuffer(buf);
- vertex_count += buf->getVertexCount();
- }
- }
+ driver->setTransform(video::ETS_WORLD, m);
+ driver->drawMeshBuffer(buf);
+ vertex_count += buf->getVertexCount();
}
+
g_profiler->avg(prefix + "draw meshes [ms]", draw.stop(true));
// Log only on solid pass because values are the same
@@ -440,8 +482,13 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
g_profiler->avg("renderMap(): animated meshes [#]", mesh_animate_count);
}
+ if (pass == scene::ESNRP_TRANSPARENT) {
+ g_profiler->avg("renderMap(): transparent buffers [#]", draw_order.size());
+ }
+
g_profiler->avg(prefix + "vertices drawn [#]", vertex_count);
g_profiler->avg(prefix + "drawcalls [#]", drawcall_count);
+ g_profiler->avg(prefix + "material swaps [#]", material_swaps);
}
static bool getVisibleBrightness(Map *map, const v3f &p0, v3f dir, float step,
diff --git a/src/client/clientmap.h b/src/client/clientmap.h
index 97ce8d355..b4dc42395 100644
--- a/src/client/clientmap.h
+++ b/src/client/clientmap.h
@@ -87,10 +87,18 @@ public:
void updateCamera(const v3f &pos, const v3f &dir, f32 fov, const v3s16 &offset)
{
+ v3s16 previous_block = getContainerPos(floatToInt(m_camera_position, BS) + m_camera_offset, MAP_BLOCKSIZE);
+
m_camera_position = pos;
m_camera_direction = dir;
m_camera_fov = fov;
m_camera_offset = offset;
+
+ v3s16 current_block = getContainerPos(floatToInt(m_camera_position, BS) + m_camera_offset, MAP_BLOCKSIZE);
+
+ // reorder the blocks when camera crosses block boundary
+ if (previous_block != current_block)
+ m_needs_update_drawlist = true;
}
/*
@@ -122,6 +130,8 @@ public:
v3s16 *p_blocks_min, v3s16 *p_blocks_max, float range=-1.0f);
void updateDrawList();
void updateDrawListShadow(const v3f &shadow_light_pos, const v3f &shadow_light_dir, float shadow_range);
+ // Returns true if draw list needs updating before drawing the next frame.
+ bool needsUpdateDrawList() { return m_needs_update_drawlist; }
void renderMap(video::IVideoDriver* driver, s32 pass);
void renderMapShadows(video::IVideoDriver *driver,
@@ -140,6 +150,23 @@ public:
f32 getCameraFov() const { return m_camera_fov; }
private:
+ // Orders blocks by distance to the camera
+ class MapBlockComparer
+ {
+ public:
+ MapBlockComparer(const v3s16 &camera_block) : m_camera_block(camera_block) {}
+
+ bool operator() (const v3s16 &left, const v3s16 &right) const
+ {
+ auto distance_left = left.getDistanceFromSQ(m_camera_block);
+ auto distance_right = right.getDistanceFromSQ(m_camera_block);
+ return distance_left > distance_right || (distance_left == distance_right && left > right);
+ }
+
+ private:
+ v3s16 m_camera_block;
+ };
+
Client *m_client;
RenderingEngine *m_rendering_engine;
@@ -153,8 +180,9 @@ private:
f32 m_camera_fov = M_PI;
v3s16 m_camera_offset;
- std::map<v3s16, MapBlock*> m_drawlist;
+ std::map<v3s16, MapBlock*, MapBlockComparer> m_drawlist;
std::map<v3s16, MapBlock*> m_drawlist_shadow;
+ bool m_needs_update_drawlist;
std::set<v2s16> m_last_drawn_sectors;
diff --git a/src/client/game.cpp b/src/client/game.cpp
index f7fd7abf9..a6448f40d 100644
--- a/src/client/game.cpp
+++ b/src/client/game.cpp
@@ -3897,8 +3897,8 @@ void Game::updateFrame(ProfilerGraph *graph, RunStats *stats, f32 dtime,
v3f camera_direction = camera->getDirection();
if (runData.update_draw_list_timer >= update_draw_list_delta
|| runData.update_draw_list_last_cam_dir.getDistanceFrom(camera_direction) > 0.2
- || m_camera_offset_changed) {
-
+ || m_camera_offset_changed
+ || client->getEnv().getClientMap().needsUpdateDrawList()) {
runData.update_draw_list_timer = 0;
client->getEnv().getClientMap().updateDrawList();
runData.update_draw_list_last_cam_dir = camera_direction;