Пример #1
0
void ClientMap::updateDrawList(video::IVideoDriver* driver, float dtime, unsigned int max_cycle_ms)
{
    ScopeProfiler sp(g_profiler, "CM::updateDrawList()", SPT_AVG);
    //g_profiler->add("CM::updateDrawList() count", 1);
    TimeTaker timer_step("ClientMap::updateDrawList");

    INodeDefManager *nodemgr = m_gamedef->ndef();

    if (!m_drawlist_last)
        m_drawlist_current = !m_drawlist_current;
    auto & drawlist = m_drawlist_current ? m_drawlist_1 : m_drawlist_0;

    if (!max_cycle_ms)
        max_cycle_ms = 300/getControl().fps_wanted;

    m_camera_mutex.Lock();
    v3f camera_position = m_camera_position;
    f32 camera_fov = m_camera_fov;
    //v3s16 camera_offset = m_camera_offset;
    m_camera_mutex.Unlock();

    // Use a higher fov to accomodate faster camera movements.
    // Blocks are cropped better when they are drawn.
    // Or maybe they aren't? Well whatever.
    camera_fov *= 1.2;

    v3s16 cam_pos_nodes = floatToInt(camera_position, BS);
    v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
    v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
    v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
    // Take a fair amount as we will be dropping more out later
    // Umm... these additions are a bit strange but they are needed.
    v3s16 p_blocks_min(
        p_nodes_min.X / MAP_BLOCKSIZE - 3,
        p_nodes_min.Y / MAP_BLOCKSIZE - 3,
        p_nodes_min.Z / MAP_BLOCKSIZE - 3);
    v3s16 p_blocks_max(
        p_nodes_max.X / MAP_BLOCKSIZE + 1,
        p_nodes_max.Y / MAP_BLOCKSIZE + 1,
        p_nodes_max.Z / MAP_BLOCKSIZE + 1);

    // Number of blocks in rendering range
    u32 blocks_in_range = 0;
    // Number of blocks occlusion culled
    u32 blocks_occlusion_culled = 0;
    // Number of blocks in rendering range but don't have a mesh
    u32 blocks_in_range_without_mesh = 0;
    // Blocks that had mesh that would have been drawn according to
    // rendering range (if max blocks limit didn't kick in)
    u32 blocks_would_have_drawn = 0;
    // Blocks that were drawn and had a mesh
    u32 blocks_drawn = 0;
    // Blocks which had a corresponding meshbuffer for this pass
    //u32 blocks_had_pass_meshbuf = 0;
    // Blocks from which stuff was actually drawn
    //u32 blocks_without_stuff = 0;
    // Distance to farthest drawn block
    float farthest_drawn = 0;
    int m_mesh_queued = 0;

    bool free_move = g_settings->getBool("free_move");

    float range_max = 100000 * BS;
    if(m_control.range_all == false)
        range_max = m_control.wanted_range * BS;

    if (draw_nearest.empty()) {
        //ScopeProfiler sp(g_profiler, "CM::updateDrawList() make list", SPT_AVG);
        TimeTaker timer_step("ClientMap::updateDrawList make list");

        auto lock = m_blocks.try_lock_shared_rec();
        if (!lock->owns_lock())
            return;

        draw_nearest.clear();

        for(auto & ir : m_blocks) {
            auto bp = ir.first;

            if(m_control.range_all == false)
            {
                if(bp.X < p_blocks_min.X
                        || bp.X > p_blocks_max.X
                        || bp.Z > p_blocks_max.Z
                        || bp.Z < p_blocks_min.Z
                        || bp.Y < p_blocks_min.Y
                        || bp.Y > p_blocks_max.Y)
                    continue;
            }

            v3s16 blockpos_nodes = bp * MAP_BLOCKSIZE;
            // Block center position
            v3f blockpos(
                ((float)blockpos_nodes.X + MAP_BLOCKSIZE/2) * BS,
                ((float)blockpos_nodes.Y + MAP_BLOCKSIZE/2) * BS,
                ((float)blockpos_nodes.Z + MAP_BLOCKSIZE/2) * BS
            );

            f32 d = radius_box(blockpos, camera_position); //blockpos_relative.getLength();
            if (d> range_max)
                continue;
            int range = d / (MAP_BLOCKSIZE * BS);
            draw_nearest.emplace_back(std::make_pair(bp, range));
        }
    }

    const int maxq = 1000;

    // No occlusion culling when free_move is on and camera is
    // inside ground
    bool occlusion_culling_enabled = true;
    if(free_move) {
        MapNode n = getNodeNoEx(cam_pos_nodes);
        if(n.getContent() == CONTENT_IGNORE ||
                nodemgr->get(n).solidness == 2)
            occlusion_culling_enabled = false;
    }

    u32 calls = 0, end_ms = porting::getTimeMs() + u32(max_cycle_ms);

    std::unordered_map<v3POS, bool, v3POSHash, v3POSEqual> occlude_cache;

    while (!draw_nearest.empty()) {
        auto ir = draw_nearest.back();

        auto bp = ir.first;
        int range = ir.second;
        draw_nearest.pop_back();
        ++calls;

        //auto block = getBlockNoCreateNoEx(bp);
        auto block = m_blocks.get(bp);
        if (!block)
            continue;

        int mesh_step = getFarmeshStep(m_control, getNodeBlockPos(cam_pos_nodes), bp);
        /*
        	Compare block position to camera position, skip
        	if not seen on display
        */

        auto mesh = block->getMesh(mesh_step);
        if (mesh)
            mesh->updateCameraOffset(m_camera_offset);

        blocks_in_range++;

        auto smesh_size = block->mesh_size;
        /*
        	Ignore if mesh doesn't exist
        */
        {
            if(!mesh) {
                blocks_in_range_without_mesh++;
                if (m_mesh_queued < maxq || range <= 2) {
                    m_client->addUpdateMeshTask(bp, false);
                    ++m_mesh_queued;
                }
                continue;
            }
            if(mesh_step == mesh->step && block->getTimestamp() <= mesh->timestamp && !smesh_size) {
                blocks_in_range_without_mesh++;
                continue;
            }
        }

        /*
        	Occlusion culling
        */

        v3s16 cpn = bp * MAP_BLOCKSIZE;
        cpn += v3s16(MAP_BLOCKSIZE/2, MAP_BLOCKSIZE/2, MAP_BLOCKSIZE/2);

        float step = BS*1;
        float stepfac = 1.2;
        float startoff = BS*1;
        float endoff = -BS*MAP_BLOCKSIZE; //*1.42; //*1.42;
        v3s16 spn = cam_pos_nodes + v3s16(0,0,0);
        s16 bs2 = MAP_BLOCKSIZE/2 + 1;
        u32 needed_count = 1;
        if( range > 1 && smesh_size &&
                occlusion_culling_enabled &&
                isOccluded(this, spn, cpn + v3s16(0,0,0),
                           step, stepfac, startoff, endoff, needed_count, nodemgr, occlude_cache) &&
                isOccluded(this, spn, cpn + v3s16(bs2,bs2,bs2),
                           step, stepfac, startoff, endoff, needed_count, nodemgr, occlude_cache) &&
                isOccluded(this, spn, cpn + v3s16(bs2,bs2,-bs2),
                           step, stepfac, startoff, endoff, needed_count, nodemgr, occlude_cache) &&
                isOccluded(this, spn, cpn + v3s16(bs2,-bs2,bs2),
                           step, stepfac, startoff, endoff, needed_count, nodemgr, occlude_cache) &&
                isOccluded(this, spn, cpn + v3s16(bs2,-bs2,-bs2),
                           step, stepfac, startoff, endoff, needed_count, nodemgr, occlude_cache) &&
                isOccluded(this, spn, cpn + v3s16(-bs2,bs2,bs2),
                           step, stepfac, startoff, endoff, needed_count, nodemgr, occlude_cache) &&
                isOccluded(this, spn, cpn + v3s16(-bs2,bs2,-bs2),
                           step, stepfac, startoff, endoff, needed_count, nodemgr, occlude_cache) &&
                isOccluded(this, spn, cpn + v3s16(-bs2,-bs2,bs2),
                           step, stepfac, startoff, endoff, needed_count, nodemgr, occlude_cache) &&
                isOccluded(this, spn, cpn + v3s16(-bs2,-bs2,-bs2),
                           step, stepfac, startoff, endoff, needed_count, nodemgr, occlude_cache)
          )
        {
            blocks_occlusion_culled++;
            continue;
        }

        // This block is in range. Reset usage timer.
        block->resetUsageTimer();

        // Limit block count in case of a sudden increase
        blocks_would_have_drawn++;
        /*
        			if(blocks_drawn >= m_control.wanted_max_blocks
        					&& m_control.range_all == false
        					&& d > m_control.wanted_min_range * BS)
        				continue;
        */

        if (mesh_step != mesh->step && (m_mesh_queued < maxq*1.2 || range <= 2)) {
            m_client->addUpdateMeshTask(bp);
            ++m_mesh_queued;
        }
        if (block->getTimestamp() > mesh->timestamp + (smesh_size ? 0 : range >= 1 ? 60 : 5) && (m_mesh_queued < maxq*1.5 || range <= 2)) {
            m_client->addUpdateMeshTaskWithEdge(bp);
            ++m_mesh_queued;
        }

        if(!smesh_size)
            continue;

        mesh->incrementUsageTimer(dtime);

        // Add to set
        //block->refGrab();
        block->resetUsageTimer();
        drawlist.set(bp, block);

        blocks_drawn++;

        if(range * MAP_BLOCKSIZE > farthest_drawn)
            farthest_drawn = range * MAP_BLOCKSIZE;

        if (farthest_drawn > m_control.farthest_drawn)
            m_control.farthest_drawn = farthest_drawn;

        if (porting::getTimeMs() > end_ms) {
            break;
        }

    }
    m_drawlist_last = draw_nearest.size();

    //if (m_drawlist_last) infostream<<"breaked UDL "<<m_drawlist_last<<" collected="<<drawlist.size()<<" calls="<<calls<<" s="<<m_blocks.size()<<" maxms="<<max_cycle_ms<<" fw="<<getControl().fps_wanted<<" morems="<<porting::getTimeMs() - end_ms<< " meshq="<<m_mesh_queued<<" occache="<<occlude_cache.size()<<std::endl;

    if (m_drawlist_last)
        return;

    //for (auto & ir : *m_drawlist)
    //	ir.second->refDrop();

    auto m_drawlist_old = !m_drawlist_current ? &m_drawlist_1 : &m_drawlist_0;
    m_drawlist = m_drawlist_current ? &m_drawlist_1 : &m_drawlist_0;
    m_drawlist_old->clear();

    m_control.blocks_would_have_drawn = blocks_would_have_drawn;
    m_control.blocks_drawn = blocks_drawn;
    m_control.farthest_drawn = farthest_drawn;

    g_profiler->avg("CM: blocks total", m_blocks.size());
    g_profiler->avg("CM: blocks in range", blocks_in_range);
    g_profiler->avg("CM: blocks occlusion culled", blocks_occlusion_culled);
    if(blocks_in_range != 0)
        g_profiler->avg("CM: blocks in range without mesh (frac)",
                        (float)blocks_in_range_without_mesh/blocks_in_range);
    g_profiler->avg("CM: blocks drawn", blocks_drawn);
    g_profiler->avg("CM: farthest drawn", farthest_drawn);
    g_profiler->avg("CM: wanted max blocks", m_control.wanted_max_blocks);
}
Пример #2
0
u32 Map::timerUpdate(float uptime, float unload_timeout, u32 max_loaded_blocks,
                     unsigned int max_cycle_ms,
                     std::vector<v3POS> *unloaded_blocks) {
	bool save_before_unloading = (mapType() == MAPTYPE_SERVER);

	// Profile modified reasons
	Profiler modprofiler;

	if (/*!m_blocks_update_last && */ m_blocks_delete->size() > 1000) {
		m_blocks_delete = (m_blocks_delete == &m_blocks_delete_1 ? &m_blocks_delete_2 : &m_blocks_delete_1);
		verbosestream << "Deleting blocks=" << m_blocks_delete->size() << std::endl;
		for (auto & ir : *m_blocks_delete)
			delete ir.first;
		m_blocks_delete->clear();
		getBlockCacheFlush();
	}

	u32 deleted_blocks_count = 0;
	u32 saved_blocks_count = 0;
	u32 block_count_all = 0;

	u32 n = 0, calls = 0, end_ms = porting::getTimeMs() + max_cycle_ms;

	std::vector<MapBlockP> blocks_delete;
	int save_started = 0;
	{
		auto lock = m_blocks.try_lock_shared_rec();
		if (!lock->owns_lock())
			return m_blocks_update_last;

#if !ENABLE_THREADS
		auto lock_map = m_nothread_locker.try_lock_unique_rec();
		if (!lock_map->owns_lock())
			return m_blocks_update_last;
#endif

		for(auto ir : m_blocks) {
			if (n++ < m_blocks_update_last) {
				continue;
			} else {
				m_blocks_update_last = 0;
			}
			++calls;

			auto block = ir.second;
			if (!block)
				continue;

			{
				auto lock = block->try_lock_unique_rec();
				if (!lock->owns_lock())
					continue;
				if(block->getUsageTimer() > unload_timeout) { // block->refGet() <= 0 &&
					v3POS p = block->getPos();
					//infostream<<" deleting block p="<<p<<" ustimer="<<block->getUsageTimer() <<" to="<< unload_timeout<<" inc="<<(uptime - block->m_uptime_timer_last)<<" state="<<block->getModified()<<std::endl;
					// Save if modified
					if (block->getModified() != MOD_STATE_CLEAN && save_before_unloading) {
						//modprofiler.add(block->getModifiedReasonString(), 1);
						if(!save_started++)
							beginSave();
						if (!saveBlock(block))
							continue;
						saved_blocks_count++;
					}

					blocks_delete.push_back(block);

					if(unloaded_blocks)
						unloaded_blocks->push_back(p);

					deleted_blocks_count++;
				} else {

#ifndef SERVER
					if (block->mesh_old)
						block->mesh_old = nullptr;
#endif

					if (!block->m_uptime_timer_last)  // not very good place, but minimum modifications
						block->m_uptime_timer_last = uptime - 0.1;
					block->incrementUsageTimer(uptime - block->m_uptime_timer_last);
					block->m_uptime_timer_last = uptime;

					block_count_all++;

					/*#ifndef SERVER
									if(block->refGet() == 0 && block->getUsageTimer() >
											g_settings->getFloat("unload_unused_meshes_timeout"))
									{
										if(block->mesh){
											delete block->mesh;
											block->mesh = NULL;
										}
									}
					#endif*/
				}

			} // block lock

			if (porting::getTimeMs() > end_ms) {
				m_blocks_update_last = n;
				break;
			}

		}
	}
	if(save_started)
		endSave();

	if (!calls)
		m_blocks_update_last = 0;

	for (auto & block : blocks_delete)
		this->deleteBlock(block);

	// Finally delete the empty sectors

	if(deleted_blocks_count != 0) {
		if (m_blocks_update_last)
			infostream << "ServerMap: timerUpdate(): Blocks processed:" << calls << "/" << m_blocks.size() << " to " << m_blocks_update_last << std::endl;
		PrintInfo(infostream); // ServerMap/ClientMap:
		infostream << "Unloaded " << deleted_blocks_count << "/" << (block_count_all + deleted_blocks_count)
		           << " blocks from memory";
		infostream << " (deleteq1=" << m_blocks_delete_1.size() << " deleteq2=" << m_blocks_delete_2.size() << ")";
		if(saved_blocks_count)
			infostream << ", of which " << saved_blocks_count << " were written";
		/*
				infostream<<", "<<block_count_all<<" blocks in memory";
		*/
		infostream << "." << std::endl;
		if(saved_blocks_count != 0) {
			PrintInfo(infostream); // ServerMap/ClientMap:
			//infostream<<"Blocks modified by: "<<std::endl;
			modprofiler.print(infostream);
		}
	}
	return m_blocks_update_last;
}