Skip to content

Commit f504070

Browse files
authoredSep 27, 2021
Order drawlist by distance to the camera when rendering (#11651)
1 parent d51d0f3 commit f504070

File tree

3 files changed

+104
-29
lines changed

3 files changed

+104
-29
lines changed
 

‎src/client/clientmap.cpp

+73-26
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,8 @@ ClientMap::ClientMap(
7373
rendering_engine->get_scene_manager(), id),
7474
m_client(client),
7575
m_rendering_engine(rendering_engine),
76-
m_control(control)
76+
m_control(control),
77+
m_drawlist(MapBlockComparer(v3s16(0,0,0)))
7778
{
7879

7980
/*
@@ -164,6 +165,8 @@ void ClientMap::updateDrawList()
164165
{
165166
ScopeProfiler sp(g_profiler, "CM::updateDrawList()", SPT_AVG);
166167

168+
m_needs_update_drawlist = false;
169+
167170
for (auto &i : m_drawlist) {
168171
MapBlock *block = i.second;
169172
block->refDrop();
@@ -178,6 +181,7 @@ void ClientMap::updateDrawList()
178181
const f32 camera_fov = m_camera_fov * 1.1f;
179182

180183
v3s16 cam_pos_nodes = floatToInt(camera_position, BS);
184+
181185
v3s16 p_blocks_min;
182186
v3s16 p_blocks_max;
183187
getBlocksInViewRange(cam_pos_nodes, &p_blocks_min, &p_blocks_max);
@@ -202,6 +206,8 @@ void ClientMap::updateDrawList()
202206
occlusion_culling_enabled = false;
203207
}
204208

209+
v3s16 camera_block = getContainerPos(cam_pos_nodes, MAP_BLOCKSIZE);
210+
m_drawlist = std::map<v3s16, MapBlock*, MapBlockComparer>(MapBlockComparer(camera_block));
205211

206212
// Uncomment to debug occluded blocks in the wireframe mode
207213
// TODO: Include this as a flag for an extended debugging setting
@@ -321,7 +327,20 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
321327
Draw the selected MapBlocks
322328
*/
323329

324-
MeshBufListList drawbufs;
330+
MeshBufListList grouped_buffers;
331+
332+
struct DrawDescriptor {
333+
v3s16 m_pos;
334+
scene::IMeshBuffer *m_buffer;
335+
bool m_reuse_material;
336+
337+
DrawDescriptor(const v3s16 &pos, scene::IMeshBuffer *buffer, bool reuse_material) :
338+
m_pos(pos), m_buffer(buffer), m_reuse_material(reuse_material)
339+
{}
340+
};
341+
342+
std::vector<DrawDescriptor> draw_order;
343+
video::SMaterial previous_material;
325344

326345
for (auto &i : m_drawlist) {
327346
v3s16 block_pos = i.first;
@@ -386,62 +405,90 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
386405
material.setFlag(video::EMF_WIREFRAME,
387406
m_control.show_wireframe);
388407

389-
drawbufs.add(buf, block_pos, layer);
408+
if (is_transparent_pass) {
409+
// Same comparison as in MeshBufListList
410+
bool new_material = material.getTexture(0) != previous_material.getTexture(0) ||
411+
material != previous_material;
412+
413+
draw_order.emplace_back(block_pos, buf, !new_material);
414+
415+
if (new_material)
416+
previous_material = material;
417+
}
418+
else {
419+
grouped_buffers.add(buf, block_pos, layer);
420+
}
390421
}
391422
}
392423
}
393424
}
394425
}
395426

427+
// Capture draw order for all solid meshes
428+
for (auto &lists : grouped_buffers.lists) {
429+
for (MeshBufList &list : lists) {
430+
// iterate in reverse to draw closest blocks first
431+
for (auto it = list.bufs.rbegin(); it != list.bufs.rend(); ++it) {
432+
draw_order.emplace_back(it->first, it->second, it != list.bufs.rbegin());
433+
}
434+
}
435+
}
436+
396437
TimeTaker draw("Drawing mesh buffers");
397438

398439
core::matrix4 m; // Model matrix
399440
v3f offset = intToFloat(m_camera_offset, BS);
441+
u32 material_swaps = 0;
400442

401-
// Render all layers in order
402-
for (auto &lists : drawbufs.lists) {
403-
for (MeshBufList &list : lists) {
404-
// Check and abort if the machine is swapping a lot
405-
if (draw.getTimerTime() > 2000) {
406-
infostream << "ClientMap::renderMap(): Rendering took >2s, " <<
407-
"returning." << std::endl;
408-
return;
409-
}
443+
// Render all mesh buffers in order
444+
drawcall_count += draw_order.size();
445+
for (auto &descriptor : draw_order) {
446+
scene::IMeshBuffer *buf = descriptor.m_buffer;
410447

448+
// Check and abort if the machine is swapping a lot
449+
if (draw.getTimerTime() > 2000) {
450+
infostream << "ClientMap::renderMap(): Rendering took >2s, " <<
451+
"returning." << std::endl;
452+
return;
453+
}
454+
455+
if (!descriptor.m_reuse_material) {
456+
auto &material = buf->getMaterial();
411457
// pass the shadow map texture to the buffer texture
412458
ShadowRenderer *shadow = m_rendering_engine->get_shadow_renderer();
413459
if (shadow && shadow->is_active()) {
414-
auto &layer = list.m.TextureLayer[3];
460+
auto &layer = material.TextureLayer[3];
415461
layer.Texture = shadow->get_texture();
416462
layer.TextureWrapU = video::E_TEXTURE_CLAMP::ETC_CLAMP_TO_EDGE;
417463
layer.TextureWrapV = video::E_TEXTURE_CLAMP::ETC_CLAMP_TO_EDGE;
418464
layer.TrilinearFilter = true;
419465
}
466+
driver->setMaterial(material);
467+
++material_swaps;
468+
}
420469

421-
driver->setMaterial(list.m);
422-
423-
drawcall_count += list.bufs.size();
424-
for (auto &pair : list.bufs) {
425-
scene::IMeshBuffer *buf = pair.second;
470+
v3f block_wpos = intToFloat(descriptor.m_pos * MAP_BLOCKSIZE, BS);
471+
m.setTranslation(block_wpos - offset);
426472

427-
v3f block_wpos = intToFloat(pair.first * MAP_BLOCKSIZE, BS);
428-
m.setTranslation(block_wpos - offset);
429-
430-
driver->setTransform(video::ETS_WORLD, m);
431-
driver->drawMeshBuffer(buf);
432-
vertex_count += buf->getVertexCount();
433-
}
434-
}
473+
driver->setTransform(video::ETS_WORLD, m);
474+
driver->drawMeshBuffer(buf);
475+
vertex_count += buf->getVertexCount();
435476
}
477+
436478
g_profiler->avg(prefix + "draw meshes [ms]", draw.stop(true));
437479

438480
// Log only on solid pass because values are the same
439481
if (pass == scene::ESNRP_SOLID) {
440482
g_profiler->avg("renderMap(): animated meshes [#]", mesh_animate_count);
441483
}
442484

485+
if (pass == scene::ESNRP_TRANSPARENT) {
486+
g_profiler->avg("renderMap(): transparent buffers [#]", draw_order.size());
487+
}
488+
443489
g_profiler->avg(prefix + "vertices drawn [#]", vertex_count);
444490
g_profiler->avg(prefix + "drawcalls [#]", drawcall_count);
491+
g_profiler->avg(prefix + "material swaps [#]", material_swaps);
445492
}
446493

447494
static bool getVisibleBrightness(Map *map, const v3f &p0, v3f dir, float step,

‎src/client/clientmap.h

+29-1
Original file line numberDiff line numberDiff line change
@@ -87,10 +87,18 @@ class ClientMap : public Map, public scene::ISceneNode
8787

8888
void updateCamera(const v3f &pos, const v3f &dir, f32 fov, const v3s16 &offset)
8989
{
90+
v3s16 previous_block = getContainerPos(floatToInt(m_camera_position, BS) + m_camera_offset, MAP_BLOCKSIZE);
91+
9092
m_camera_position = pos;
9193
m_camera_direction = dir;
9294
m_camera_fov = fov;
9395
m_camera_offset = offset;
96+
97+
v3s16 current_block = getContainerPos(floatToInt(m_camera_position, BS) + m_camera_offset, MAP_BLOCKSIZE);
98+
99+
// reorder the blocks when camera crosses block boundary
100+
if (previous_block != current_block)
101+
m_needs_update_drawlist = true;
94102
}
95103

96104
/*
@@ -122,6 +130,8 @@ class ClientMap : public Map, public scene::ISceneNode
122130
v3s16 *p_blocks_min, v3s16 *p_blocks_max, float range=-1.0f);
123131
void updateDrawList();
124132
void updateDrawListShadow(const v3f &shadow_light_pos, const v3f &shadow_light_dir, float shadow_range);
133+
// Returns true if draw list needs updating before drawing the next frame.
134+
bool needsUpdateDrawList() { return m_needs_update_drawlist; }
125135
void renderMap(video::IVideoDriver* driver, s32 pass);
126136

127137
void renderMapShadows(video::IVideoDriver *driver,
@@ -140,6 +150,23 @@ class ClientMap : public Map, public scene::ISceneNode
140150
f32 getCameraFov() const { return m_camera_fov; }
141151

142152
private:
153+
// Orders blocks by distance to the camera
154+
class MapBlockComparer
155+
{
156+
public:
157+
MapBlockComparer(const v3s16 &camera_block) : m_camera_block(camera_block) {}
158+
159+
bool operator() (const v3s16 &left, const v3s16 &right) const
160+
{
161+
auto distance_left = left.getDistanceFromSQ(m_camera_block);
162+
auto distance_right = right.getDistanceFromSQ(m_camera_block);
163+
return distance_left > distance_right || (distance_left == distance_right && left > right);
164+
}
165+
166+
private:
167+
v3s16 m_camera_block;
168+
};
169+
143170
Client *m_client;
144171
RenderingEngine *m_rendering_engine;
145172

@@ -153,8 +180,9 @@ class ClientMap : public Map, public scene::ISceneNode
153180
f32 m_camera_fov = M_PI;
154181
v3s16 m_camera_offset;
155182

156-
std::map<v3s16, MapBlock*> m_drawlist;
183+
std::map<v3s16, MapBlock*, MapBlockComparer> m_drawlist;
157184
std::map<v3s16, MapBlock*> m_drawlist_shadow;
185+
bool m_needs_update_drawlist;
158186

159187
std::set<v2s16> m_last_drawn_sectors;
160188

‎src/client/game.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -3897,8 +3897,8 @@ void Game::updateFrame(ProfilerGraph *graph, RunStats *stats, f32 dtime,
38973897
v3f camera_direction = camera->getDirection();
38983898
if (runData.update_draw_list_timer >= update_draw_list_delta
38993899
|| runData.update_draw_list_last_cam_dir.getDistanceFrom(camera_direction) > 0.2
3900-
|| m_camera_offset_changed) {
3901-
3900+
|| m_camera_offset_changed
3901+
|| client->getEnv().getClientMap().needsUpdateDrawList()) {
39023902
runData.update_draw_list_timer = 0;
39033903
client->getEnv().getClientMap().updateDrawList();
39043904
runData.update_draw_list_last_cam_dir = camera_direction;

0 commit comments

Comments
 (0)
Please sign in to comment.