On-demand item meshes and textures
authorPerttu Ahola <celeron55@gmail.com>
Fri, 30 Nov 2012 16:12:32 +0000 (18:12 +0200)
committerPerttu Ahola <celeron55@gmail.com>
Sat, 1 Dec 2012 22:38:26 +0000 (00:38 +0200)
src/camera.cpp
src/client.cpp
src/content_cao.cpp
src/game.cpp
src/guiFormSpecMenu.cpp
src/itemdef.cpp
src/itemdef.h
src/util/container.h

index 43f26cd8f911c492e531a5c4aa3d82599df8078e..0c391f10dc880daccac7fc2549720c6923ac05b1 100644 (file)
@@ -529,7 +529,7 @@ void Camera::setDigging(s32 button)
 void Camera::wield(const ItemStack &item)
 {
        IItemDefManager *idef = m_gamedef->idef();
-       scene::IMesh *wield_mesh = item.getDefinition(idef).wield_mesh;
+       scene::IMesh *wield_mesh = idef->getWieldMesh(item.getDefinition(idef).name, m_gamedef);
        if(wield_mesh)
        {
                m_wieldnode->setMesh(wield_mesh);
index ce84933c942feb0a315786bbd535bae44b314514..3c69db076121b0eb01e751d1fa96e2932a5fb32e 100644 (file)
@@ -1468,8 +1468,8 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
 
                int num_files = readU16(is);
                
-               verbosestream<<"Client received TOCLIENT_ANNOUNCE_MEDIA ("
-                               <<num_files<<" files)"<<std::endl;
+               infostream<<"Client: Received media announcement: packet size: "
+                               <<datasize<<std::endl;
 
                core::list<MediaRequest> file_requests;
 
@@ -2464,10 +2464,6 @@ void Client::afterContentReceived()
        infostream<<"- Updating node textures"<<std::endl;
        m_nodedef->updateTextures(m_tsrc);
 
-       // Update item textures and meshes
-       infostream<<"- Updating item textures and meshes"<<std::endl;
-       m_itemdef->updateTexturesAndMeshes(this);
-
        // Start mesh update thread after setting up content definitions
        infostream<<"- Starting mesh update thread"<<std::endl;
        m_mesh_update_thread.Start();
index 8229ded62daec26eb3d3597b38e4ea9d360de490..d71911b952e681d2505fa48aa32f9a1bc2dd8be0 100644 (file)
@@ -472,7 +472,7 @@ void ItemCAO::updateTexture()
                IItemDefManager *idef = m_gamedef->idef();
                ItemStack item;
                item.deSerialize(is, idef);
-               texture = item.getDefinition(idef).inventory_texture;
+               texture = idef->getInventoryTexture(item.getDefinition(idef).name, m_gamedef);
        }
        catch(SerializationError &e)
        {
@@ -957,7 +957,7 @@ public:
                                infostream<<"textures[0]: "<<m_prop.textures[0]<<std::endl;
                                IItemDefManager *idef = m_gamedef->idef();
                                ItemStack item(m_prop.textures[0], 1, 0, "", idef);
-                               scene::IMesh *item_mesh = item.getDefinition(idef).wield_mesh;
+                               scene::IMesh *item_mesh = idef->getWieldMesh(item.getDefinition(idef).name, m_gamedef);
                                
                                // Copy mesh to be able to set unique vertex colors
                                scene::IMeshManipulator *manip =
index 086293894924bb6637e8ce0b4069c9ca42010b6e..a38ffa13cac3270f69d3ee6695f99236634d243a 100644 (file)
@@ -1416,11 +1416,12 @@ void the_game(
                        g_gamecallback->changepassword_requested = false;
                }
 
-               /*
-                       Process TextureSource's queue
-               */
+               /* Process TextureSource's queue */
                tsrc->processQueue();
 
+               /* Process ItemDefManager's queue */
+               itemdef->processQueue(gamedef);
+
                /*
                        Random calculations
                */
index 66ac4c08f73540fef8d08099ca90c9b2bf60f594..986f30494fc34c946f333a9c07d4163a9d09458e 100644 (file)
@@ -46,7 +46,7 @@ void drawItemStack(video::IVideoDriver *driver,
                return;
        
        const ItemDefinition &def = item.getDefinition(gamedef->idef());
-       video::ITexture *texture = def.inventory_texture;
+       video::ITexture *texture = gamedef->idef()->getInventoryTexture(def.name, gamedef);
 
        // Draw the inventory texture
        if(texture != NULL)
@@ -519,7 +519,7 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
                        IItemDefManager *idef = m_gamedef->idef();
                        ItemStack item;
                        item.deSerialize(fimage, idef);
-                       video::ITexture *texture = item.getDefinition(idef).inventory_texture;
+                       video::ITexture *texture = idef->getInventoryTexture(item.getDefinition(idef).name, m_gamedef);
                        std::string tooltip = item.getDefinition(idef).description;
                        FieldSpec spec = FieldSpec(
                                narrow_to_wide(fname.c_str()),
index da33fd32295daae5fceb2c4e6c6f3d1159337ddd..9ce064ea4e480df75184288f43a275d898d9c9ab 100644 (file)
@@ -31,6 +31,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #endif
 #include "log.h"
 #include "util/serialize.h"
+#include "util/container.h"
+#include "util/thread.h"
 #include <map>
 #include <set>
 
@@ -71,14 +73,6 @@ ItemDefinition& ItemDefinition::operator=(const ItemDefinition &def)
        }
        groups = def.groups;
        node_placement_prediction = def.node_placement_prediction;
-#ifndef SERVER
-       inventory_texture = def.inventory_texture;
-       if(def.wield_mesh)
-       {
-               wield_mesh = def.wield_mesh;
-               wield_mesh->grab();
-       }
-#endif
        return *this;
 }
 
@@ -91,10 +85,6 @@ void ItemDefinition::resetInitial()
 {
        // Initialize pointers to NULL so reset() does not delete undefined pointers
        tool_capabilities = NULL;
-#ifndef SERVER
-       inventory_texture = NULL;
-       wield_mesh = NULL;
-#endif
        reset();
 }
 
@@ -117,15 +107,6 @@ void ItemDefinition::reset()
        groups.clear();
 
        node_placement_prediction = "";
-       
-#ifndef SERVER
-       inventory_texture = NULL;
-       if(wield_mesh)
-       {
-               wield_mesh->drop();
-               wield_mesh = NULL;
-       }
-#endif
 }
 
 void ItemDefinition::serialize(std::ostream &os) const
@@ -203,13 +184,39 @@ void ItemDefinition::deSerialize(std::istream &is)
 
 class CItemDefManager: public IWritableItemDefManager
 {
+#ifndef SERVER
+       struct ClientCached
+       {
+               video::ITexture *inventory_texture;
+               scene::IMesh *wield_mesh;
+
+               ClientCached():
+                       inventory_texture(NULL),
+                       wield_mesh(NULL)
+               {}
+       };
+#endif
+
 public:
        CItemDefManager()
        {
+#ifndef SERVER
+               m_main_thread = get_current_thread_id();
+#endif
+       
                clear();
        }
        virtual ~CItemDefManager()
        {
+#ifndef SERVER
+               const core::list<ClientCached*> &values = m_clientcached.getValues();
+               for(core::list<ClientCached*>::ConstIterator
+                               i = values.begin(); i != values.end(); ++i)
+               {
+                       ClientCached *cc = *i;
+                       cc->wield_mesh->drop();
+               }
+#endif
        }
        virtual const ItemDefinition& get(const std::string &name_) const
        {
@@ -256,6 +263,220 @@ public:
                std::map<std::string, ItemDefinition*>::const_iterator i;
                return m_item_definitions.find(name) != m_item_definitions.end();
        }
+#ifndef SERVER
+       ClientCached* createClientCachedDirect(const std::string &name,
+                       IGameDef *gamedef) const
+       {
+               infostream<<"Lazily creating item texture and mesh for \""
+                               <<name<<"\""<<std::endl;
+
+               // This is not thread-safe
+               assert(get_current_thread_id() == m_main_thread);
+
+               // Skip if already in cache
+               ClientCached *cc = NULL;
+               m_clientcached.get(name, &cc);
+               if(cc)
+                       return cc;
+
+               ITextureSource *tsrc = gamedef->getTextureSource();
+               INodeDefManager *nodedef = gamedef->getNodeDefManager();
+               IrrlichtDevice *device = tsrc->getDevice();
+               video::IVideoDriver *driver = device->getVideoDriver();
+               const ItemDefinition *def = &get(name);
+
+               // Create new ClientCached
+               cc = new ClientCached();
+
+               bool need_node_mesh = false;
+
+               // Create an inventory texture
+               cc->inventory_texture = NULL;
+               if(def->inventory_image != "")
+               {
+                       cc->inventory_texture = tsrc->getTextureRaw(def->inventory_image);
+               }
+               else if(def->type == ITEM_NODE)
+               {
+                       need_node_mesh = true;
+               }
+
+               // Create a wield mesh
+               if(cc->wield_mesh != NULL)
+               {
+                       cc->wield_mesh->drop();
+                       cc->wield_mesh = NULL;
+               }
+               if(def->type == ITEM_NODE && def->wield_image == "")
+               {
+                       need_node_mesh = true;
+               }
+               else if(def->wield_image != "" || def->inventory_image != "")
+               {
+                       // Extrude the wield image into a mesh
+
+                       std::string imagename;
+                       if(def->wield_image != "")
+                               imagename = def->wield_image;
+                       else
+                               imagename = def->inventory_image;
+
+                       cc->wield_mesh = createExtrudedMesh(
+                                       tsrc->getTextureRaw(imagename),
+                                       driver,
+                                       def->wield_scale * v3f(40.0, 40.0, 4.0));
+                       if(cc->wield_mesh == NULL)
+                       {
+                               infostream<<"ItemDefManager: WARNING: "
+                                       <<"updateTexturesAndMeshes(): "
+                                       <<"Unable to create extruded mesh for item "
+                                       <<def->name<<std::endl;
+                       }
+               }
+
+               if(need_node_mesh)
+               {
+                       /*
+                               Get node properties
+                       */
+                       content_t id = nodedef->getId(def->name);
+                       const ContentFeatures &f = nodedef->get(id);
+
+                       u8 param1 = 0;
+                       if(f.param_type == CPT_LIGHT)
+                               param1 = 0xee;
+
+                       /*
+                               Make a mesh from the node
+                       */
+                       MeshMakeData mesh_make_data(gamedef);
+                       MapNode mesh_make_node(id, param1, 0);
+                       mesh_make_data.fillSingleNode(&mesh_make_node);
+                       MapBlockMesh mapblock_mesh(&mesh_make_data);
+
+                       scene::IMesh *node_mesh = mapblock_mesh.getMesh();
+                       assert(node_mesh);
+                       setMeshColor(node_mesh, video::SColor(255, 255, 255, 255));
+
+                       /*
+                               Scale and translate the mesh so it's a unit cube
+                               centered on the origin
+                       */
+                       scaleMesh(node_mesh, v3f(1.0/BS, 1.0/BS, 1.0/BS));
+                       translateMesh(node_mesh, v3f(-1.0, -1.0, -1.0));
+
+                       /*
+                               Draw node mesh into a render target texture
+                       */
+                       if(cc->inventory_texture == NULL)
+                       {
+                               core::dimension2d<u32> dim(64,64);
+                               std::string rtt_texture_name = "INVENTORY_"
+                                       + def->name + "_RTT";
+                               v3f camera_position(0, 1.0, -1.5);
+                               camera_position.rotateXZBy(45);
+                               v3f camera_lookat(0, 0, 0);
+                               core::CMatrix4<f32> camera_projection_matrix;
+                               // Set orthogonal projection
+                               camera_projection_matrix.buildProjectionMatrixOrthoLH(
+                                               1.65, 1.65, 0, 100);
+
+                               video::SColorf ambient_light(0.2,0.2,0.2);
+                               v3f light_position(10, 100, -50);
+                               video::SColorf light_color(0.5,0.5,0.5);
+                               f32 light_radius = 1000;
+
+                               cc->inventory_texture = generateTextureFromMesh(
+                                       node_mesh, device, dim, rtt_texture_name,
+                                       camera_position,
+                                       camera_lookat,
+                                       camera_projection_matrix,
+                                       ambient_light,
+                                       light_position,
+                                       light_color,
+                                       light_radius);
+
+                               // render-to-target didn't work
+                               if(cc->inventory_texture == NULL)
+                               {
+                                       cc->inventory_texture =
+                                               tsrc->getTextureRaw(f.tiledef[0].name);
+                               }
+                       }
+
+                       /*
+                               Use the node mesh as the wield mesh
+                       */
+                       if(cc->wield_mesh == NULL)
+                       {
+                               // Scale to proper wield mesh proportions
+                               scaleMesh(node_mesh, v3f(30.0, 30.0, 30.0)
+                                               * def->wield_scale);
+                               cc->wield_mesh = node_mesh;
+                               cc->wield_mesh->grab();
+                       }
+
+                       // falling outside of here deletes node_mesh
+               }
+
+               // Put in cache
+               m_clientcached.set(name, cc);
+
+               return cc;
+       }
+       ClientCached* getClientCached(const std::string &name,
+                       IGameDef *gamedef) const
+       {
+               ClientCached *cc = NULL;
+               m_clientcached.get(name, &cc);
+               if(cc)
+                       return cc;
+
+               if(get_current_thread_id() == m_main_thread)
+               {
+                       return createClientCachedDirect(name, gamedef);
+               }
+               else
+               {
+                       // We're gonna ask the result to be put into here
+                       ResultQueue<std::string, ClientCached*, u8, u8> result_queue;
+                       // Throw a request in
+                       m_get_clientcached_queue.add(name, 0, 0, &result_queue);
+                       try{
+                               // Wait result for a second
+                               GetResult<std::string, ClientCached*, u8, u8>
+                                               result = result_queue.pop_front(1000);
+                               // Check that at least something worked OK
+                               assert(result.key == name);
+                               // Return it
+                               return result.item;
+                       }
+                       catch(ItemNotFoundException &e)
+                       {
+                               errorstream<<"Waiting for clientcached timed out."<<std::endl;
+                               return &m_dummy_clientcached;
+                       }
+               }
+       }
+       // Get item inventory texture
+       virtual video::ITexture* getInventoryTexture(const std::string &name,
+                       IGameDef *gamedef) const
+       {
+               ClientCached *cc = getClientCached(name, gamedef);
+               if(!cc)
+                       return NULL;
+               return cc->inventory_texture;
+       }
+       // Get item wield mesh
+       virtual scene::IMesh* getWieldMesh(const std::string &name,
+                       IGameDef *gamedef) const
+       {
+               ClientCached *cc = getClientCached(name, gamedef);
+               if(!cc)
+                       return NULL;
+               return cc->wield_mesh;
+       }
+#endif
        void clear()
        {
                for(std::map<std::string, ItemDefinition*>::const_iterator
@@ -321,157 +542,6 @@ public:
                        m_aliases[name] = convert_to;
                }
        }
-
-       virtual void updateTexturesAndMeshes(IGameDef *gamedef)
-       {
-#ifndef SERVER
-               infostream<<"ItemDefManager::updateTexturesAndMeshes(): Updating "
-                               <<"textures and meshes in item definitions"<<std::endl;
-
-               ITextureSource *tsrc = gamedef->getTextureSource();
-               INodeDefManager *nodedef = gamedef->getNodeDefManager();
-               IrrlichtDevice *device = tsrc->getDevice();
-               video::IVideoDriver *driver = device->getVideoDriver();
-
-               for(std::map<std::string, ItemDefinition*>::iterator
-                               i = m_item_definitions.begin();
-                               i != m_item_definitions.end(); i++)
-               {
-                       ItemDefinition *def = i->second;
-
-                       bool need_node_mesh = false;
-
-                       // Create an inventory texture
-                       def->inventory_texture = NULL;
-                       if(def->inventory_image != "")
-                       {
-                               def->inventory_texture = tsrc->getTextureRaw(def->inventory_image);
-                       }
-                       else if(def->type == ITEM_NODE)
-                       {
-                               need_node_mesh = true;
-                       }
-
-                       // Create a wield mesh
-                       if(def->wield_mesh != NULL)
-                       {
-                               def->wield_mesh->drop();
-                               def->wield_mesh = NULL;
-                       }
-                       if(def->type == ITEM_NODE && def->wield_image == "")
-                       {
-                               need_node_mesh = true;
-                       }
-                       else if(def->wield_image != "" || def->inventory_image != "")
-                       {
-                               // Extrude the wield image into a mesh
-
-                               std::string imagename;
-                               if(def->wield_image != "")
-                                       imagename = def->wield_image;
-                               else
-                                       imagename = def->inventory_image;
-
-                               def->wield_mesh = createExtrudedMesh(
-                                               tsrc->getTextureRaw(imagename),
-                                               driver,
-                                               def->wield_scale * v3f(40.0, 40.0, 4.0));
-                               if(def->wield_mesh == NULL)
-                               {
-                                       infostream<<"ItemDefManager: WARNING: "
-                                               <<"updateTexturesAndMeshes(): "
-                                               <<"Unable to create extruded mesh for item "
-                                               <<def->name<<std::endl;
-                               }
-                       }
-
-                       if(need_node_mesh)
-                       {
-                               /*
-                                       Get node properties
-                               */
-                               content_t id = nodedef->getId(def->name);
-                               const ContentFeatures &f = nodedef->get(id);
-
-                               u8 param1 = 0;
-                               if(f.param_type == CPT_LIGHT)
-                                       param1 = 0xee;
-
-                               /*
-                                       Make a mesh from the node
-                               */
-                               MeshMakeData mesh_make_data(gamedef);
-                               MapNode mesh_make_node(id, param1, 0);
-                               mesh_make_data.fillSingleNode(&mesh_make_node);
-                               MapBlockMesh mapblock_mesh(&mesh_make_data);
-
-                               scene::IMesh *node_mesh = mapblock_mesh.getMesh();
-                               assert(node_mesh);
-                               setMeshColor(node_mesh, video::SColor(255, 255, 255, 255));
-
-                               /*
-                                       Scale and translate the mesh so it's a unit cube
-                                       centered on the origin
-                               */
-                               scaleMesh(node_mesh, v3f(1.0/BS, 1.0/BS, 1.0/BS));
-                               translateMesh(node_mesh, v3f(-1.0, -1.0, -1.0));
-
-                               /*
-                                       Draw node mesh into a render target texture
-                               */
-                               if(def->inventory_texture == NULL)
-                               {
-                                       core::dimension2d<u32> dim(64,64);
-                                       std::string rtt_texture_name = "INVENTORY_"
-                                               + def->name + "_RTT";
-                                       v3f camera_position(0, 1.0, -1.5);
-                                       camera_position.rotateXZBy(45);
-                                       v3f camera_lookat(0, 0, 0);
-                                       core::CMatrix4<f32> camera_projection_matrix;
-                                       // Set orthogonal projection
-                                       camera_projection_matrix.buildProjectionMatrixOrthoLH(
-                                                       1.65, 1.65, 0, 100);
-
-                                       video::SColorf ambient_light(0.2,0.2,0.2);
-                                       v3f light_position(10, 100, -50);
-                                       video::SColorf light_color(0.5,0.5,0.5);
-                                       f32 light_radius = 1000;
-
-                                       def->inventory_texture = generateTextureFromMesh(
-                                               node_mesh, device, dim, rtt_texture_name,
-                                               camera_position,
-                                               camera_lookat,
-                                               camera_projection_matrix,
-                                               ambient_light,
-                                               light_position,
-                                               light_color,
-                                               light_radius);
-
-                                       // render-to-target didn't work
-                                       if(def->inventory_texture == NULL)
-                                       {
-                                               def->inventory_texture =
-                                                       tsrc->getTextureRaw(f.tiledef[0].name);
-                                       }
-                               }
-
-                               /*
-                                       Use the node mesh as the wield mesh
-                               */
-                               if(def->wield_mesh == NULL)
-                               {
-                                       // Scale to proper wield mesh proportions
-                                       scaleMesh(node_mesh, v3f(30.0, 30.0, 30.0)
-                                                       * def->wield_scale);
-                                       def->wield_mesh = node_mesh;
-                                       def->wield_mesh->grab();
-                               }
-
-                               // falling outside of here deletes node_mesh
-                       }
-               }
-#endif
-       }
        void serialize(std::ostream &os)
        {
                writeU8(os, 0); // version
@@ -521,11 +591,37 @@ public:
                        registerAlias(name, convert_to);
                }
        }
+       void processQueue(IGameDef *gamedef)
+       {
+#ifndef SERVER
+               while(m_get_clientcached_queue.size() > 0)
+               {
+                       GetRequest<std::string, ClientCached*, u8, u8>
+                                       request = m_get_clientcached_queue.pop();
+                       GetResult<std::string, ClientCached*, u8, u8>
+                                       result;
+                       result.key = request.key;
+                       result.callers = request.callers;
+                       result.item = createClientCachedDirect(request.key, gamedef);
+                       request.dest->push_back(result);
+               }
+#endif
+       }
 private:
        // Key is name
        std::map<std::string, ItemDefinition*> m_item_definitions;
        // Aliases
        std::map<std::string, std::string> m_aliases;
+#ifndef SERVER
+       // The id of the thread that is allowed to use irrlicht directly
+       threadid_t m_main_thread;
+       // A reference to this can be returned when nothing is found, to avoid NULLs
+       mutable ClientCached m_dummy_clientcached;
+       // Cached textures and meshes
+       mutable MutexedMap<std::string, ClientCached*> m_clientcached;
+       // Queued clientcached fetches (to be processed by the main thread)
+       mutable RequestQueue<std::string, ClientCached*, u8, u8> m_get_clientcached_queue;
+#endif
 };
 
 IWritableItemDefManager* createItemDefManager()
index d1ac52bb8c82b8ffcffbf0dea65fd0665bb9ae01..699d727bde4a60334073f854b64aa6717d413322 100644 (file)
@@ -72,14 +72,6 @@ struct ItemDefinition
        // "" = no prediction
        std::string node_placement_prediction;
 
-       /*
-               Cached stuff
-       */
-#ifndef SERVER
-       video::ITexture *inventory_texture;
-       scene::IMesh *wield_mesh;
-#endif
-
        /*
                Some helpful methods
        */
@@ -108,6 +100,14 @@ public:
        virtual std::set<std::string> getAll() const=0;
        // Check if item is known
        virtual bool isKnown(const std::string &name) const=0;
+#ifndef SERVER
+       // Get item inventory texture
+       virtual video::ITexture* getInventoryTexture(const std::string &name,
+                       IGameDef *gamedef) const=0;
+       // Get item wield mesh
+       virtual scene::IMesh* getWieldMesh(const std::string &name,
+               IGameDef *gamedef) const=0;
+#endif
 
        virtual void serialize(std::ostream &os)=0;
 };
@@ -126,6 +126,14 @@ public:
        virtual std::set<std::string> getAll() const=0;
        // Check if item is known
        virtual bool isKnown(const std::string &name) const=0;
+#ifndef SERVER
+       // Get item inventory texture
+       virtual video::ITexture* getInventoryTexture(const std::string &name,
+                       IGameDef *gamedef) const=0;
+       // Get item wield mesh
+       virtual scene::IMesh* getWieldMesh(const std::string &name,
+               IGameDef *gamedef) const=0;
+#endif
 
        // Remove all registered item and node definitions and aliases
        // Then re-add the builtin item definitions
@@ -138,15 +146,11 @@ public:
        virtual void registerAlias(const std::string &name,
                        const std::string &convert_to)=0;
 
-       /*
-               Update inventory textures and wield meshes to latest
-               return values of ITextureSource and INodeDefManager.
-               Call after updating the texture atlas of a texture source.
-       */
-       virtual void updateTexturesAndMeshes(IGameDef *gamedef)=0;
-
        virtual void serialize(std::ostream &os)=0;
        virtual void deSerialize(std::istream &is)=0;
+
+       // Do stuff asked by threads that can only be done in the main thread
+       virtual void processQueue(IGameDef *gamedef)=0;
 };
 
 IWritableItemDefManager* createItemDefManager();
index bdf5609798513a4b0f028642beb7d94ad6b70450..8c1ae02fbf1c5ddfb224b4006b0de3bf3665f48a 100644 (file)
@@ -107,6 +107,17 @@ public:
                return true;
        }
 
+       core::list<Value> getValues()
+       {
+               core::list<Value> result;
+               for(typename core::map<Key, Value>::Iterator
+                               i = m_values.getIterator();
+                               i.atEnd() == false; i++){
+                       result.push_back(i.getNode()->getValue());
+               }
+               return result;
+       }
+
 private:
        core::map<Key, Value> m_values;
        JMutex m_mutex;