MapBlock *EmergeThread::finishGen(v3s16 pos, BlockMakeData *bmdata, std::map<v3s16, MapBlock *> *modified_blocks) { //MutexAutoLock envlock(m_server->m_env_mutex); ScopeProfiler sp(g_profiler, "EmergeThread: after Mapgen::makeChunk", SPT_AVG); /* Perform post-processing on blocks (invalidate lighting, queue liquid transforms, etc.) to finish block make */ m_map->finishBlockMake(bmdata, modified_blocks); MapBlock *block = m_map->getBlockNoCreateNoEx(pos, false, true); if (!block) { errorstream << "EmergeThread::finishGen: Couldn't grab block we " "just generated: " << PP(pos) << std::endl; return NULL; } v3s16 minp = bmdata->blockpos_min * MAP_BLOCKSIZE; v3s16 maxp = bmdata->blockpos_max * MAP_BLOCKSIZE + v3s16(1,1,1) * (MAP_BLOCKSIZE - 1); // Ignore map edit events, they will not need to be sent // to anybody because the block hasn't been sent to anybody /* thread unsafe MapEditEventAreaIgnorer ign( &m_server->m_ignore_map_edit_events_area, VoxelArea(minp, maxp)); */ /* Run Lua on_generated callbacks */ try { MAP_NOTHREAD_LOCK(m_map); m_server->getScriptIface()->environment_OnGenerated( minp, maxp, m_mapgen->blockseed); } catch (LuaError &e) { m_server->setAsyncFatalError("Lua: " + std::string(e.what())); } EMERGE_DBG_OUT("ended up with: " << analyze_block(block)); /* Activate the block */ m_server->m_env->activateBlock(block, 0); return block; }
static int symtable_analyze(struct symtable *st) { PyObject *free, *global; int r; free = PyDict_New(); if (!free) return 0; global = PyDict_New(); if (!global) { Py_DECREF(free); return 0; } r = analyze_block(st->st_top, NULL, free, global); Py_DECREF(free); Py_DECREF(global); return r; }
static int analyze_child_block(PySTEntryObject *entry, PyObject *bound, PyObject *free, PyObject *global, PyObject* child_free) { PyObject *temp_bound = NULL, *temp_global = NULL, *temp_free = NULL; /* Copy the bound and global dictionaries. These dictionaries are used by all blocks enclosed by the current block. The analyze_block() call modifies these dictionaries. */ temp_bound = PyDict_New(); if (!temp_bound) goto error; if (PyDict_Update(temp_bound, bound) < 0) goto error; temp_free = PyDict_New(); if (!temp_free) goto error; if (PyDict_Update(temp_free, free) < 0) goto error; temp_global = PyDict_New(); if (!temp_global) goto error; if (PyDict_Update(temp_global, global) < 0) goto error; if (!analyze_block(entry, temp_bound, temp_free, temp_global)) goto error; if (PyDict_Update(child_free, temp_free) < 0) goto error; Py_DECREF(temp_bound); Py_DECREF(temp_free); Py_DECREF(temp_global); return 1; error: Py_XDECREF(temp_bound); Py_XDECREF(temp_free); Py_XDECREF(temp_global); return 0; }
void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass) { DSTACK(__FUNCTION_NAME); bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT; std::string prefix; if(pass == scene::ESNRP_SOLID) prefix = "CM: solid: "; else prefix = "CM: transparent: "; /* This is called two times per frame, reset on the non-transparent one */ if(pass == scene::ESNRP_SOLID) { m_last_drawn_sectors.clear(); } bool use_trilinear_filter = g_settings->getBool("trilinear_filter"); bool use_bilinear_filter = g_settings->getBool("bilinear_filter"); bool use_anisotropic_filter = g_settings->getBool("anisotropic_filter"); /* Get time for measuring timeout. Measuring time is very useful for long delays when the machine is swapping a lot. */ int time1 = time(0); /* Get animation parameters */ float animation_time = m_client->getAnimationTime(); int crack = m_client->getCrackLevel(); u32 daynight_ratio = m_client->getEnv().getDayNightRatio(); m_camera_mutex.Lock(); v3f camera_position = m_camera_position; v3f camera_direction = m_camera_direction; f32 camera_fov = m_camera_fov; m_camera_mutex.Unlock(); /* Get all blocks and draw all visible ones */ 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); u32 vertex_count = 0; u32 meshbuffer_count = 0; // For limiting number of mesh animations per frame u32 mesh_animate_count = 0; u32 mesh_animate_count_far = 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; /* Draw the selected MapBlocks */ { ScopeProfiler sp(g_profiler, prefix+"drawing blocks", SPT_AVG); MeshBufListList drawbufs; for(std::map<v3s16, MapBlock*>::iterator i = m_drawlist.begin(); i != m_drawlist.end(); ++i) { MapBlock *block = i->second; // If the mesh of the block happened to get deleted, ignore it if(block->mesh == NULL) continue; float d = 0.0; if(isBlockInSight(block->getPos(), camera_position, camera_direction, camera_fov, 100000*BS, &d) == false) { continue; } // Mesh animation { //JMutexAutoLock lock(block->mesh_mutex); MapBlockMesh *mapBlockMesh = block->mesh; assert(mapBlockMesh); // Pretty random but this should work somewhat nicely bool faraway = d >= BS*50; //bool faraway = d >= m_control.wanted_range * BS; if(mapBlockMesh->isAnimationForced() || !faraway || mesh_animate_count_far < (m_control.range_all ? 200 : 50)) { bool animated = mapBlockMesh->animate( faraway, animation_time, crack, daynight_ratio); if(animated) mesh_animate_count++; if(animated && faraway) mesh_animate_count_far++; } else { mapBlockMesh->decreaseAnimationForceTimer(); } } /* Get the meshbuffers of the block */ { //JMutexAutoLock lock(block->mesh_mutex); MapBlockMesh *mapBlockMesh = block->mesh; assert(mapBlockMesh); scene::SMesh *mesh = mapBlockMesh->getMesh(); assert(mesh); u32 c = mesh->getMeshBufferCount(); for(u32 i=0; i<c; i++) { scene::IMeshBuffer *buf = mesh->getMeshBuffer(i); buf->getMaterial().setFlag(video::EMF_TRILINEAR_FILTER, use_trilinear_filter); buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, use_bilinear_filter); buf->getMaterial().setFlag(video::EMF_ANISOTROPIC_FILTER, use_anisotropic_filter); const video::SMaterial& material = buf->getMaterial(); video::IMaterialRenderer* rnd = driver->getMaterialRenderer(material.MaterialType); bool transparent = (rnd && rnd->isTransparent()); if(transparent == is_transparent_pass) { if(buf->getVertexCount() == 0) errorstream<<"Block ["<<analyze_block(block) <<"] contains an empty meshbuf"<<std::endl; drawbufs.add(buf); } } } } std::list<MeshBufList> &lists = drawbufs.lists; int timecheck_counter = 0; for(std::list<MeshBufList>::iterator i = lists.begin(); i != lists.end(); ++i) { { timecheck_counter++; if(timecheck_counter > 50) { timecheck_counter = 0; int time2 = time(0); if(time2 > time1 + 4) { infostream<<"ClientMap::renderMap(): " "Rendering takes ages, returning." <<std::endl; return; } } } MeshBufList &list = *i; driver->setMaterial(list.m); for(std::list<scene::IMeshBuffer*>::iterator j = list.bufs.begin(); j != list.bufs.end(); ++j) { scene::IMeshBuffer *buf = *j; driver->drawMeshBuffer(buf); vertex_count += buf->getVertexCount(); meshbuffer_count++; } #if 0 /* Draw the faces of the block */ { //JMutexAutoLock lock(block->mesh_mutex); MapBlockMesh *mapBlockMesh = block->mesh; assert(mapBlockMesh); scene::SMesh *mesh = mapBlockMesh->getMesh(); assert(mesh); u32 c = mesh->getMeshBufferCount(); bool stuff_actually_drawn = false; for(u32 i=0; i<c; i++) { scene::IMeshBuffer *buf = mesh->getMeshBuffer(i); const video::SMaterial& material = buf->getMaterial(); video::IMaterialRenderer* rnd = driver->getMaterialRenderer(material.MaterialType); bool transparent = (rnd && rnd->isTransparent()); // Render transparent on transparent pass and likewise. if(transparent == is_transparent_pass) { if(buf->getVertexCount() == 0) errorstream<<"Block ["<<analyze_block(block) <<"] contains an empty meshbuf"<<std::endl; /* This *shouldn't* hurt too much because Irrlicht doesn't change opengl textures if the old material has the same texture. */ driver->setMaterial(buf->getMaterial()); driver->drawMeshBuffer(buf); vertex_count += buf->getVertexCount(); meshbuffer_count++; stuff_actually_drawn = true; } } if(stuff_actually_drawn) blocks_had_pass_meshbuf++; else blocks_without_stuff++; } #endif } } // ScopeProfiler // Log only on solid pass because values are the same if(pass == scene::ESNRP_SOLID){ g_profiler->avg("CM: animated meshes", mesh_animate_count); g_profiler->avg("CM: animated meshes (far)", mesh_animate_count_far); } g_profiler->avg(prefix+"vertices drawn", vertex_count); if(blocks_had_pass_meshbuf != 0) g_profiler->avg(prefix+"meshbuffers per block", (float)meshbuffer_count / (float)blocks_had_pass_meshbuf); if(blocks_drawn != 0) g_profiler->avg(prefix+"empty blocks (frac)", (float)blocks_without_stuff / blocks_drawn); /*infostream<<"renderMap(): is_transparent_pass="******", rendered "<<vertex_count<<" vertices."<<std::endl;*/ }
void *EmergeThread::Thread() { ThreadStarted(); log_register_thread("EmergeThread" + itos(id)); DSTACK(__FUNCTION_NAME); BEGIN_DEBUG_EXCEPTION_HANDLER v3s16 last_tried_pos(-32768,-32768,-32768); // For error output v3s16 p; u8 flags = 0; map = (ServerMap *)&(m_server->m_env->getMap()); emerge = m_server->m_emerge; mapgen = emerge->mapgen[id]; enable_mapgen_debug_info = emerge->mapgen_debug_info; porting::setThreadName("EmergeThread"); while (!StopRequested()) try { if (!popBlockEmerge(&p, &flags)) { qevent.wait(); continue; } last_tried_pos = p; if (blockpos_over_limit(p)) continue; bool allow_generate = flags & BLOCK_EMERGE_ALLOWGEN; EMERGE_DBG_OUT("p=" PP(p) " allow_generate=" << allow_generate); /* Try to fetch block from memory or disk. If not found and asked to generate, initialize generator. */ BlockMakeData data; MapBlock *block = NULL; std::map<v3s16, MapBlock *> modified_blocks; if (getBlockOrStartGen(p, &block, &data, allow_generate) && mapgen) { { ScopeProfiler sp(g_profiler, "EmergeThread: Mapgen::makeChunk", SPT_AVG); TimeTaker t("mapgen::make_block()"); mapgen->makeChunk(&data); if (enable_mapgen_debug_info == false) t.stop(true); // Hide output } { //envlock: usually 0ms, but can take either 30 or 400ms to acquire JMutexAutoLock envlock(m_server->m_env_mutex); ScopeProfiler sp(g_profiler, "EmergeThread: after " "Mapgen::makeChunk (envlock)", SPT_AVG); map->finishBlockMake(&data, modified_blocks); block = map->getBlockNoCreateNoEx(p); if (block) { /* Do some post-generate stuff */ v3s16 minp = data.blockpos_min * MAP_BLOCKSIZE; v3s16 maxp = data.blockpos_max * MAP_BLOCKSIZE + v3s16(1,1,1) * (MAP_BLOCKSIZE - 1); // Ignore map edit events, they will not need to be sent // to anybody because the block hasn't been sent to anybody MapEditEventAreaIgnorer ign(&m_server->m_ignore_map_edit_events_area, VoxelArea(minp, maxp)); try { // takes about 90ms with -O1 on an e3-1230v2 m_server->getScriptIface()->environment_OnGenerated( minp, maxp, emerge->getBlockSeed(minp)); } catch(LuaError &e) { m_server->setAsyncFatalError(e.what()); } EMERGE_DBG_OUT("ended up with: " << analyze_block(block)); m_server->m_env->activateBlock(block, 0); } } } /* Set sent status of modified blocks on clients */ // Add the originally fetched block to the modified list if (block) modified_blocks[p] = block; if (modified_blocks.size() > 0) { m_server->SetBlocksNotSent(modified_blocks); } } catch (VersionMismatchException &e) { std::ostringstream err; err << "World data version mismatch in MapBlock "<<PP(last_tried_pos)<<std::endl; err << "----"<<std::endl; err << "\""<<e.what()<<"\""<<std::endl; err << "See debug.txt."<<std::endl; err << "World probably saved by a newer version of Minetest."<<std::endl; m_server->setAsyncFatalError(err.str()); } catch (SerializationError &e) { std::ostringstream err; err << "Invalid data in MapBlock "<<PP(last_tried_pos)<<std::endl; err << "----"<<std::endl; err << "\""<<e.what()<<"\""<<std::endl; err << "See debug.txt."<<std::endl; err << "You can ignore this using [ignore_world_load_errors = true]."<<std::endl; m_server->setAsyncFatalError(err.str()); } END_DEBUG_EXCEPTION_HANDLER(errorstream) log_deregister_thread(); return NULL; }
void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass) { DSTACK(__FUNCTION_NAME); bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT; std::string prefix; if(pass == scene::ESNRP_SOLID) prefix = "CM: solid: "; else prefix = "CM: transparent: "; //ScopeProfiler sp(g_profiler, "CM::renderMap() " + prefix, SPT_AVG); /* Get time for measuring timeout. Measuring time is very useful for long delays when the machine is swapping a lot. */ //int time1 = time(0); /* Get animation parameters */ float animation_time = m_client->getAnimationTime(); int crack = m_client->getCrackLevel(); u32 daynight_ratio = m_client->getEnv().getDayNightRatio(); m_camera_mutex.Lock(); v3f camera_position = m_camera_position; f32 camera_fov = m_camera_fov * 1.1; m_camera_mutex.Unlock(); /* Get all blocks and draw all visible ones */ v3s16 cam_pos_nodes = floatToInt(camera_position, BS); u32 vertex_count = 0; u32 meshbuffer_count = 0; // For limiting number of mesh animations per frame u32 mesh_animate_count = 0; u32 mesh_animate_count_far = 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; /* Draw the selected MapBlocks */ { //ScopeProfiler sp(g_profiler, prefix+"drawing blocks", SPT_AVG); MeshBufListList drawbufs; std::vector<MapBlock::mesh_type> used_meshes; //keep shared_ptr auto drawlist = m_drawlist.load(); auto lock = drawlist->lock_shared_rec(); used_meshes.reserve(drawlist->size()); //g_profiler->add("CM::renderMap()cnt"+ prefix, drawlist->size()); for(auto & ir : *drawlist) { auto block = ir.second; int mesh_step = getFarmeshStep(m_control, getNodeBlockPos(cam_pos_nodes), block->getPos()); // If the mesh of the block happened to get deleted, ignore it auto mapBlockMesh = block->getMesh(mesh_step); if (!mapBlockMesh) continue; float d = 0.0; if(isBlockInSight(block->getPos(), camera_position, m_camera_direction, camera_fov, 100000*BS, &d) == false) { continue; } used_meshes.emplace_back(mapBlockMesh); // Mesh animation { //JMutexAutoLock lock(block->mesh_mutex); mapBlockMesh->updateCameraOffset(m_camera_offset); // Pretty random but this should work somewhat nicely bool faraway = d >= BS*50; //bool faraway = d >= m_control.wanted_range * BS; if(mapBlockMesh->isAnimationForced() || !faraway || mesh_animate_count_far < (m_control.range_all ? 200 : 50)) { bool animated = mapBlockMesh->animate( faraway, animation_time, crack, daynight_ratio); if(animated) mesh_animate_count++; if(animated && faraway) mesh_animate_count_far++; } else { mapBlockMesh->decreaseAnimationForceTimer(); } } /* Get the meshbuffers of the block */ { //JMutexAutoLock lock(block->mesh_mutex); auto *mesh = mapBlockMesh->getMesh(); if (!mesh) continue; u32 c = mesh->getMeshBufferCount(); for(u32 i=0; i<c; i++) { scene::IMeshBuffer *buf = mesh->getMeshBuffer(i); buf->getMaterial().setFlag(video::EMF_TRILINEAR_FILTER, m_cache_trilinear_filter); buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, m_cache_bilinear_filter); buf->getMaterial().setFlag(video::EMF_ANISOTROPIC_FILTER, m_cache_anistropic_filter); const video::SMaterial& material = buf->getMaterial(); video::IMaterialRenderer* rnd = driver->getMaterialRenderer(material.MaterialType); bool transparent = (rnd && rnd->isTransparent()); if(transparent == is_transparent_pass) { if(buf->getVertexCount() == 0) errorstream<<"Block ["<<analyze_block(block) <<"] contains an empty meshbuf"<<std::endl; drawbufs.add(buf); } } } } std::vector<MeshBufList> &lists = drawbufs.lists; //int timecheck_counter = 0; for(std::vector<MeshBufList>::iterator i = lists.begin(); i != lists.end(); ++i) { #if 0 timecheck_counter++; if(timecheck_counter > 50) { timecheck_counter = 0; int time2 = time(0); if(time2 > time1 + 4) { infostream << "ClientMap::renderMap(): " "Rendering takes ages, returning." << std::endl; return; } } #endif MeshBufList &list = *i; driver->setMaterial(list.m); for(std::vector<scene::IMeshBuffer*>::iterator j = list.bufs.begin(); j != list.bufs.end(); ++j) { scene::IMeshBuffer *buf = *j; driver->drawMeshBuffer(buf); vertex_count += buf->getVertexCount(); meshbuffer_count++; } } } // ScopeProfiler // Log only on solid pass because values are the same if(pass == scene::ESNRP_SOLID) { g_profiler->avg("CM: animated meshes", mesh_animate_count); g_profiler->avg("CM: animated meshes (far)", mesh_animate_count_far); } g_profiler->avg(prefix+"vertices drawn", vertex_count); if(blocks_had_pass_meshbuf != 0) g_profiler->avg(prefix+"meshbuffers per block", (float)meshbuffer_count / (float)blocks_had_pass_meshbuf); if(blocks_drawn != 0) g_profiler->avg(prefix+"empty blocks (frac)", (float)blocks_without_stuff / blocks_drawn); g_profiler->avg("CM: PrimitiveDrawn", driver->getPrimitiveCountDrawn()); /*infostream<<"renderMap(): is_transparent_pass="******", rendered "<<vertex_count<<" vertices."<<std::endl;*/ }
void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass) { DSTACK(FUNCTION_NAME); bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT; std::string prefix; if (pass == scene::ESNRP_SOLID) prefix = "CM: solid: "; else prefix = "CM: transparent: "; /* This is called two times per frame, reset on the non-transparent one */ if (pass == scene::ESNRP_SOLID) m_last_drawn_sectors.clear(); /* Get time for measuring timeout. Measuring time is very useful for long delays when the machine is swapping a lot. */ std::time_t time1 = time(0); /* Get animation parameters */ float animation_time = m_client->getAnimationTime(); int crack = m_client->getCrackLevel(); u32 daynight_ratio = m_client->getEnv().getDayNightRatio(); v3f camera_position = m_camera_position; v3f camera_direction = m_camera_direction; f32 camera_fov = m_camera_fov; /* Get all blocks and draw all visible ones */ u32 vertex_count = 0; u32 meshbuffer_count = 0; // For limiting number of mesh animations per frame u32 mesh_animate_count = 0; u32 mesh_animate_count_far = 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; /* Draw the selected MapBlocks */ { ScopeProfiler sp(g_profiler, prefix + "drawing blocks", SPT_AVG); MeshBufListList drawbufs; for (std::map<v3s16, MapBlock*>::iterator i = m_drawlist.begin(); i != m_drawlist.end(); ++i) { MapBlock *block = i->second; // If the mesh of the block happened to get deleted, ignore it if (!block->mesh) continue; float d = 0.0; if (!isBlockInSight(block->getPos(), camera_position, camera_direction, camera_fov, 100000 * BS, &d)) continue; // Mesh animation if (pass == scene::ESNRP_SOLID) { //MutexAutoLock lock(block->mesh_mutex); MapBlockMesh *mapBlockMesh = block->mesh; assert(mapBlockMesh); // Pretty random but this should work somewhat nicely bool faraway = d >= BS * 50; //bool faraway = d >= m_control.wanted_range * BS; if (mapBlockMesh->isAnimationForced() || !faraway || mesh_animate_count_far < (m_control.range_all ? 200 : 50)) { bool animated = mapBlockMesh->animate(faraway, animation_time, crack, daynight_ratio); if (animated) mesh_animate_count++; if (animated && faraway) mesh_animate_count_far++; } else { mapBlockMesh->decreaseAnimationForceTimer(); } } /* Get the meshbuffers of the block */ { //MutexAutoLock lock(block->mesh_mutex); MapBlockMesh *mapBlockMesh = block->mesh; assert(mapBlockMesh); for (int layer = 0; layer < MAX_TILE_LAYERS; layer++) { scene::IMesh *mesh = mapBlockMesh->getMesh(layer); assert(mesh); u32 c = mesh->getMeshBufferCount(); for (u32 i = 0; i < c; i++) { scene::IMeshBuffer *buf = mesh->getMeshBuffer(i); video::SMaterial& material = buf->getMaterial(); video::IMaterialRenderer* rnd = driver->getMaterialRenderer(material.MaterialType); bool transparent = (rnd && rnd->isTransparent()); if (transparent == is_transparent_pass) { if (buf->getVertexCount() == 0) errorstream << "Block [" << analyze_block(block) << "] contains an empty meshbuf" << std::endl; material.setFlag(video::EMF_TRILINEAR_FILTER, m_cache_trilinear_filter); material.setFlag(video::EMF_BILINEAR_FILTER, m_cache_bilinear_filter); material.setFlag(video::EMF_ANISOTROPIC_FILTER, m_cache_anistropic_filter); material.setFlag(video::EMF_WIREFRAME, m_control.show_wireframe); drawbufs.add(buf, layer); } } } } } // Render all layers in order for (int layer = 0; layer < MAX_TILE_LAYERS; layer++) { std::vector<MeshBufList> &lists = drawbufs.lists[layer]; int timecheck_counter = 0; for (MeshBufList &list : lists) { timecheck_counter++; if (timecheck_counter > 50) { timecheck_counter = 0; std::time_t time2 = time(0); if (time2 > time1 + 4) { infostream << "ClientMap::renderMap(): " "Rendering takes ages, returning." << std::endl; return; } } driver->setMaterial(list.m); for (scene::IMeshBuffer *buf : list.bufs) { driver->drawMeshBuffer(buf); vertex_count += buf->getVertexCount(); meshbuffer_count++; } } } } // ScopeProfiler // Log only on solid pass because values are the same if (pass == scene::ESNRP_SOLID) { g_profiler->avg("CM: animated meshes", mesh_animate_count); g_profiler->avg("CM: animated meshes (far)", mesh_animate_count_far); } g_profiler->avg(prefix + "vertices drawn", vertex_count); if (blocks_had_pass_meshbuf != 0) g_profiler->avg(prefix + "meshbuffers per block", (float)meshbuffer_count / (float)blocks_had_pass_meshbuf); if (blocks_drawn != 0) g_profiler->avg(prefix + "empty blocks (frac)", (float)blocks_without_stuff / blocks_drawn); /*infostream<<"renderMap(): is_transparent_pass="******", rendered "<<vertex_count<<" vertices."<<std::endl;*/ }
void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass) { INodeDefManager *nodemgr = m_gamedef->ndef(); //m_dout<<DTIME<<"Rendering map..."<<std::endl; DSTACK(__FUNCTION_NAME); bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT; std::string prefix; if(pass == scene::ESNRP_SOLID) prefix = "CM: solid: "; else prefix = "CM: transparent: "; /* This is called two times per frame, reset on the non-transparent one */ if(pass == scene::ESNRP_SOLID) { m_last_drawn_sectors.clear(); } /* Get time for measuring timeout. Measuring time is very useful for long delays when the machine is swapping a lot. */ int time1 = time(0); /* Get animation parameters */ float animation_time = m_client->getAnimationTime(); int crack = m_client->getCrackLevel(); u32 daynight_ratio = m_client->getEnv().getDayNightRatio(); m_camera_mutex.Lock(); v3f camera_position = m_camera_position; v3f camera_direction = m_camera_direction; f32 camera_fov = m_camera_fov; m_camera_mutex.Unlock(); /* Get all blocks and draw all visible ones */ 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); u32 vertex_count = 0; u32 meshbuffer_count = 0; // For limiting number of mesh animations per frame u32 mesh_animate_count = 0; u32 mesh_animate_count_far = 0; // 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; /* Collect a set of blocks for drawing */ core::map<v3s16, MapBlock*> drawset; { ScopeProfiler sp(g_profiler, prefix+"collecting blocks for drawing", SPT_AVG); for(core::map<v2s16, MapSector*>::Iterator si = m_sectors.getIterator(); si.atEnd() == false; si++) { MapSector *sector = si.getNode()->getValue(); v2s16 sp = sector->getPos(); if(m_control.range_all == false) { if(sp.X < p_blocks_min.X || sp.X > p_blocks_max.X || sp.Y < p_blocks_min.Z || sp.Y > p_blocks_max.Z) continue; } core::list< MapBlock * > sectorblocks; sector->getBlocks(sectorblocks); /* Loop through blocks in sector */ u32 sector_blocks_drawn = 0; core::list< MapBlock * >::Iterator i; for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++) { MapBlock *block = *i; /* Compare block position to camera position, skip if not seen on display */ float range = 100000 * BS; if(m_control.range_all == false) range = m_control.wanted_range * BS; float d = 0.0; if(isBlockInSight(block->getPos(), camera_position, camera_direction, camera_fov, range, &d) == false) { continue; } // This is ugly (spherical distance limit?) /*if(m_control.range_all == false && d - 0.5*BS*MAP_BLOCKSIZE > range) continue;*/ blocks_in_range++; /* Ignore if mesh doesn't exist */ { //JMutexAutoLock lock(block->mesh_mutex); if(block->mesh == NULL){ blocks_in_range_without_mesh++; continue; } } /* Occlusion culling */ // No occlusion culling when free_move is on and camera is // inside ground bool occlusion_culling_enabled = true; if(g_settings->getBool("free_move")){ MapNode n = getNodeNoEx(cam_pos_nodes); if(n.getContent() == CONTENT_IGNORE || nodemgr->get(n).solidness == 2) occlusion_culling_enabled = false; } v3s16 cpn = block->getPos() * MAP_BLOCKSIZE; cpn += v3s16(MAP_BLOCKSIZE/2, MAP_BLOCKSIZE/2, MAP_BLOCKSIZE/2); float step = BS*1; float stepfac = 1.1; 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( occlusion_culling_enabled && isOccluded(this, spn, cpn + v3s16(0,0,0), step, stepfac, startoff, endoff, needed_count, nodemgr) && isOccluded(this, spn, cpn + v3s16(bs2,bs2,bs2), step, stepfac, startoff, endoff, needed_count, nodemgr) && isOccluded(this, spn, cpn + v3s16(bs2,bs2,-bs2), step, stepfac, startoff, endoff, needed_count, nodemgr) && isOccluded(this, spn, cpn + v3s16(bs2,-bs2,bs2), step, stepfac, startoff, endoff, needed_count, nodemgr) && isOccluded(this, spn, cpn + v3s16(bs2,-bs2,-bs2), step, stepfac, startoff, endoff, needed_count, nodemgr) && isOccluded(this, spn, cpn + v3s16(-bs2,bs2,bs2), step, stepfac, startoff, endoff, needed_count, nodemgr) && isOccluded(this, spn, cpn + v3s16(-bs2,bs2,-bs2), step, stepfac, startoff, endoff, needed_count, nodemgr) && isOccluded(this, spn, cpn + v3s16(-bs2,-bs2,bs2), step, stepfac, startoff, endoff, needed_count, nodemgr) && isOccluded(this, spn, cpn + v3s16(-bs2,-bs2,-bs2), step, stepfac, startoff, endoff, needed_count, nodemgr) ) { 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; // Mesh animation { //JMutexAutoLock lock(block->mesh_mutex); MapBlockMesh *mapBlockMesh = block->mesh; // Pretty random but this should work somewhat nicely bool faraway = d >= BS*50; //bool faraway = d >= m_control.wanted_range * BS; if(mapBlockMesh->isAnimationForced() || !faraway || mesh_animate_count_far < (m_control.range_all ? 200 : 50)) { bool animated = mapBlockMesh->animate( faraway, animation_time, crack, daynight_ratio); if(animated) mesh_animate_count++; if(animated && faraway) mesh_animate_count_far++; } else { mapBlockMesh->decreaseAnimationForceTimer(); } } // Add to set drawset[block->getPos()] = block; sector_blocks_drawn++; blocks_drawn++; } // foreach sectorblocks if(sector_blocks_drawn != 0) m_last_drawn_sectors[sp] = true; } } // ScopeProfiler /* Draw the selected MapBlocks */ { ScopeProfiler sp(g_profiler, prefix+"drawing blocks", SPT_AVG); int timecheck_counter = 0; for(core::map<v3s16, MapBlock*>::Iterator i = drawset.getIterator(); i.atEnd() == false; i++) { { timecheck_counter++; if(timecheck_counter > 50) { timecheck_counter = 0; int time2 = time(0); if(time2 > time1 + 4) { infostream<<"ClientMap::renderMap(): " "Rendering takes ages, returning." <<std::endl; return; } } } MapBlock *block = i.getNode()->getValue(); /* Draw the faces of the block */ { //JMutexAutoLock lock(block->mesh_mutex); MapBlockMesh *mapBlockMesh = block->mesh; assert(mapBlockMesh); scene::SMesh *mesh = mapBlockMesh->getMesh(); assert(mesh); u32 c = mesh->getMeshBufferCount(); bool stuff_actually_drawn = false; for(u32 i=0; i<c; i++) { scene::IMeshBuffer *buf = mesh->getMeshBuffer(i); const video::SMaterial& material = buf->getMaterial(); video::IMaterialRenderer* rnd = driver->getMaterialRenderer(material.MaterialType); bool transparent = (rnd && rnd->isTransparent()); // Render transparent on transparent pass and likewise. if(transparent == is_transparent_pass) { if(buf->getVertexCount() == 0) errorstream<<"Block ["<<analyze_block(block) <<"] contains an empty meshbuf"<<std::endl; /* This *shouldn't* hurt too much because Irrlicht doesn't change opengl textures if the old material has the same texture. */ driver->setMaterial(buf->getMaterial()); driver->drawMeshBuffer(buf); vertex_count += buf->getVertexCount(); meshbuffer_count++; stuff_actually_drawn = true; } } if(stuff_actually_drawn) blocks_had_pass_meshbuf++; else blocks_without_stuff++; } } } // ScopeProfiler // Log only on solid pass because values are the same if(pass == scene::ESNRP_SOLID){ 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: animated meshes", mesh_animate_count); g_profiler->avg("CM: animated meshes (far)", mesh_animate_count_far); } g_profiler->avg(prefix+"vertices drawn", vertex_count); if(blocks_had_pass_meshbuf != 0) g_profiler->avg(prefix+"meshbuffers per block", (float)meshbuffer_count / (float)blocks_had_pass_meshbuf); if(blocks_drawn != 0) g_profiler->avg(prefix+"empty blocks (frac)", (float)blocks_without_stuff / blocks_drawn); m_control.blocks_drawn = blocks_drawn; m_control.blocks_would_have_drawn = blocks_would_have_drawn; /*infostream<<"renderMap(): is_transparent_pass="******", rendered "<<vertex_count<<" vertices."<<std::endl;*/ }
void optimizeStores(IRUnit& unit) { PassTracer tracer{&unit, Trace::hhir_store, "optimizeStores"}; // This isn't required for correctness, but it may allow removing stores that // otherwise we would leave alone. splitCriticalEdges(unit); auto incompleteQ = dataflow_worklist<PostOrderId>(unit.numBlocks()); /* * Global state for this pass, visible while processing any block. */ auto genv = Global { unit }; auto const& poBlockList = genv.poBlockList; if (genv.ainfo.locations.size() == 0) { FTRACE(1, "no memory accesses to possibly optimize\n"); return; } FTRACE(1, "\nLocations:\n{}\n", show(genv.ainfo)); /* * Initialize the block state structures. * * Important note: every block starts with an empty liveOut set, including * blocks that are exiting the region. When an HHIR region is exited, * there's always some instruction we can use to indicate via memory_effects * what may be read (e.g. EndCatch, RetCtrl, ReqBindJmp, etc). When we start * iterating, we'll appropriately add things to GEN based on these. */ for (auto poId = uint32_t{0}; poId < poBlockList.size(); ++poId) { genv.blockStates[poBlockList[poId]->id()].id = poId; incompleteQ.push(poId); } /* * Analyze each block to compute its transfer function. * * The blockAnalysis vector is indexed by post order id. */ auto const blockAnalysis = [&] () -> jit::vector<BlockAnalysis> { auto ret = jit::vector<BlockAnalysis>{}; ret.reserve(unit.numBlocks()); for (auto id = uint32_t{0}; id < poBlockList.size(); ++id) { ret.push_back(analyze_block(genv, poBlockList[id])); } return ret; }(); FTRACE(2, "Transfer functions:\n{}\n", [&]() -> std::string { auto ret = std::string{}; for (auto poId = uint32_t{0}; poId < poBlockList.size(); ++poId) { auto& analysis = blockAnalysis[poId]; folly::format( &ret, " B{}\n" " gen: {}\n" " kill: {}\n", poBlockList[poId]->id(), show(analysis.gen), show(analysis.kill) ); } return ret; }() ); /* * Iterate on the liveOut states until we reach a fixed point. */ FTRACE(4, "Iterating\n"); while (!incompleteQ.empty()) { auto const poId = incompleteQ.pop(); auto const blk = poBlockList[poId]; auto& state = genv.blockStates[blk->id()]; auto const transfer = blockAnalysis[poId]; assertx(state.id == poId); state.liveIn = transfer.gen | (state.liveOut & ~transfer.kill); FTRACE(4, " block B{}\n" " live out: {}\n" " gen : {}\n" " kill : {}\n" " live in : {}\n", blk->id(), show(state.liveOut), show(transfer.gen), show(transfer.kill), show(state.liveIn)); /* * Update predecessors by merging the live in state into the live out state * of each predecessor. * * If anything changes, reschedule the predecessor. */ blk->forEachPred([&] (Block* pred) { FTRACE(4, " -> {}\n", pred->id()); auto& predState = genv.blockStates[pred->id()]; auto const oldLiveOut = predState.liveOut; predState.liveOut |= state.liveIn; if (predState.liveOut != oldLiveOut) { incompleteQ.push(predState.id); } }); } /* * We've reached a fixed point. Now we can act on this information to remove * dead stores. */ FTRACE(2, "\nFixed point:\n{}\n", [&]() -> std::string { auto ret = std::string{}; for (auto& blk : poBlockList) { folly::format( &ret, " B{: <3}: {}\n", blk->id(), show(genv.blockStates[blk->id()].liveOut) ); } return ret; }() ); for (auto& block : poBlockList) { optimize_block(genv, block); } }
static int analyze_block(PySTEntryObject *ste, PyObject *bound, PyObject *free, PyObject *global) { PyObject *name, *v, *local = NULL, *scope = NULL, *newbound = NULL; PyObject *newglobal = NULL, *newfree = NULL; int i, success = 0; Py_ssize_t pos = 0; local = PyDict_New(); if (!local) goto error; scope = PyDict_New(); if (!scope) goto error; newglobal = PyDict_New(); if (!newglobal) goto error; newfree = PyDict_New(); if (!newfree) goto error; newbound = PyDict_New(); if (!newbound) goto error; if (ste->ste_type == ClassBlock) { /* make a copy of globals before calling analyze_name(), because global statements in the class have no effect on nested functions. */ if (PyDict_Update(newglobal, global) < 0) goto error; if (bound) if (PyDict_Update(newbound, bound) < 0) goto error; } assert(PySTEntry_Check(ste)); assert(PyDict_Check(ste->ste_symbols)); while (PyDict_Next(ste->ste_symbols, &pos, &name, &v)) { long flags = PyInt_AS_LONG(v); if (!analyze_name(ste, scope, name, flags, bound, local, free, global)) goto error; } if (ste->ste_type != ClassBlock) { if (ste->ste_type == FunctionBlock) { if (PyDict_Update(newbound, local) < 0) goto error; } if (bound) { if (PyDict_Update(newbound, bound) < 0) goto error; } if (PyDict_Update(newglobal, global) < 0) goto error; } /* Recursively call analyze_block() on each child block */ for (i = 0; i < PyList_GET_SIZE(ste->ste_children); ++i) { PyObject *c = PyList_GET_ITEM(ste->ste_children, i); PySTEntryObject* entry; assert(c && PySTEntry_Check(c)); entry = (PySTEntryObject*)c; if (!analyze_block(entry, newbound, newfree, newglobal)) goto error; if (entry->ste_free || entry->ste_child_free) ste->ste_child_free = 1; } if (ste->ste_type == FunctionBlock && !analyze_cells(scope, newfree)) goto error; if (!update_symbols(ste->ste_symbols, scope, bound, newfree, ste->ste_type == ClassBlock)) goto error; if (!check_unoptimized(ste)) goto error; if (PyDict_Update(free, newfree) < 0) goto error; success = 1; error: Py_XDECREF(local); Py_XDECREF(scope); Py_XDECREF(newbound); Py_XDECREF(newglobal); Py_XDECREF(newfree); if (!success) assert(PyErr_Occurred()); return success; }